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 android.database.CharArrayBuffer;
20 import android.database.CursorWindow;
21 import android.database.MatrixCursor;
22 import android.database.sqlite.SQLiteException;
23 import android.os.Parcel;
24 import android.test.AndroidTestCase;
25 
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Random;
29 
30 public class CursorWindowTest extends AndroidTestCase {
31 
32     private static final String TEST_STRING = "Test String";
33 
testWriteCursorToWindow()34     public void testWriteCursorToWindow() throws Exception {
35         // create cursor
36         String[] colNames = new String[]{"_id", "name", "number", "profit"};
37         int colsize = colNames.length;
38         ArrayList<ArrayList<Integer>> list = createTestList(10, colsize);
39         MatrixCursor cursor = new MatrixCursor(colNames, list.size());
40         for (ArrayList<Integer> row : list) {
41             cursor.addRow(row);
42         }
43 
44         // fill window
45         CursorWindow window = new CursorWindow(false);
46         cursor.fillWindow(0, window);
47 
48         // read from cursor window
49         for (int i = 0; i < list.size(); i++) {
50             ArrayList<Integer> col = list.get(i);
51             for (int j = 0; j < colsize; j++) {
52                 String s = window.getString(i, j);
53                 int r2 = col.get(j);
54                 int r1 = Integer.parseInt(s);
55                 assertEquals(r2, r1);
56             }
57         }
58 
59         // test cursor window handle startpos != 0
60         window.clear();
61         cursor.fillWindow(1, window);
62         // read from cursor from window
63         for (int i = 1; i < list.size(); i++) {
64             ArrayList<Integer> col = list.get(i);
65             for (int j = 0; j < colsize; j++) {
66                 String s = window.getString(i, j);
67                 int r2 = col.get(j);
68                 int r1 = Integer.parseInt(s);
69                 assertEquals(r2, r1);
70             }
71         }
72 
73         // Clear the window and make sure it's empty
74         window.clear();
75         assertEquals(0, window.getNumRows());
76     }
77 
testNull()78     public void testNull() {
79         CursorWindow window = getOneByOneWindow();
80 
81         // Put in a null value and read it back as various types
82         assertTrue(window.putNull(0, 0));
83         assertNull(window.getString(0, 0));
84         assertEquals(0, window.getLong(0, 0));
85         assertEquals(0.0, window.getDouble(0, 0));
86         assertNull(window.getBlob(0, 0));
87     }
88 
testEmptyString()89     public void testEmptyString() {
90         CursorWindow window = getOneByOneWindow();
91 
92         // put size 0 string and read it back as various types
93         assertTrue(window.putString("", 0, 0));
94         assertEquals("", window.getString(0, 0));
95         assertEquals(0, window.getLong(0, 0));
96         assertEquals(0.0, window.getDouble(0, 0));
97     }
98 
testConstructors()99     public void testConstructors() {
100         int TEST_NUMBER = 5;
101         CursorWindow cursorWindow;
102 
103         // Test constructor with 'true' input, and getStartPosition should return 0
104         cursorWindow = new CursorWindow(true);
105         assertEquals(0, cursorWindow.getStartPosition());
106 
107         // Test constructor with 'false' input
108         cursorWindow = new CursorWindow(false);
109         assertEquals(0, cursorWindow.getStartPosition());
110 
111         // Test newFromParcel
112         Parcel parcel = Parcel.obtain();
113         cursorWindow = new CursorWindow(true);
114         cursorWindow.setStartPosition(TEST_NUMBER);
115         cursorWindow.setNumColumns(1);
116         cursorWindow.allocRow();
117         cursorWindow.putString(TEST_STRING, TEST_NUMBER, 0);
118         cursorWindow.writeToParcel(parcel, 0);
119 
120         parcel.setDataPosition(0);
121         cursorWindow = CursorWindow.CREATOR.createFromParcel(parcel);
122         assertEquals(TEST_NUMBER, cursorWindow.getStartPosition());
123         assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0));
124     }
125 
testDataStructureOperations()126     public void testDataStructureOperations() {
127         CursorWindow cursorWindow = new CursorWindow(true);
128 
129         // Test with normal values
130         assertTrue(cursorWindow.setNumColumns(0));
131         // If the column has been set to zero, can't put String.
132         assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));
133 
134         // Test allocRow().
135         assertTrue(cursorWindow.allocRow());
136         assertEquals(1, cursorWindow.getNumRows());
137         assertTrue(cursorWindow.allocRow());
138         assertEquals(2, cursorWindow.getNumRows());
139         // Though allocate a row, but the column number is still 0, so can't putString.
140         assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));
141 
142         // Test freeLstRow
143         cursorWindow.freeLastRow();
144         assertEquals(1, cursorWindow.getNumRows());
145         cursorWindow.freeLastRow();
146         assertEquals(0, cursorWindow.getNumRows());
147 
148         cursorWindow = new CursorWindow(true);
149         assertTrue(cursorWindow.setNumColumns(6));
150         assertTrue(cursorWindow.allocRow());
151         // Column number set to negative number, so now can put values.
152         assertTrue(cursorWindow.putString(TEST_STRING, 0, 0));
153         assertEquals(TEST_STRING, cursorWindow.getString(0, 0));
154 
155         // Test with negative value
156         assertFalse(cursorWindow.setNumColumns(-1));
157 
158         // Test with reference limitation
159         cursorWindow.releaseReference();
160         try {
161             cursorWindow.setNumColumns(5);
162             fail("setNumColumns() should throws IllegalStateException here.");
163         } catch (IllegalStateException e) {
164             // expected
165         }
166 
167         // Test close(), close will also minus references, that will lead acquireReference()
168         // related operation failed.
169         cursorWindow.close();
170         try {
171             cursorWindow.acquireReference();
172             fail("setNumColumns() should throws IllegalStateException here.");
173         } catch (IllegalStateException e) {
174             // expected
175         }
176     }
177 
testAccessDataValues()178     public void testAccessDataValues() {
179         final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
180         final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
181         final long NUMBER_SHORT = (short) NUMBER_INTEGER;
182         final float NUMBER_FLOAT_SCIENCE = 7.332952E11f;
183         final double NUMBER_DOUBLE_SCIENCE = 7.33295205887E11;
184         final String NUMBER_FLOAT_SCIENCE_STRING = "7.332952E11";
185         final String NUMBER_DOUBLE_SCIENCE_STRING = "7.33295205887E11";
186         final String NUMBER_FLOAT_SCIENCE_STRING2 = "7.33295e+11";
187 
188         byte[] originalBlob = new byte[Byte.MAX_VALUE];
189         for (int i = 0; i < Byte.MAX_VALUE; i++) {
190             originalBlob[i] = (byte) i;
191         }
192 
193         CursorWindow cursorWindow = new CursorWindow(true);
194         cursorWindow.setNumColumns(5);
195         cursorWindow.allocRow();
196 
197         // Test putString, getString, getLong, getInt, isBlob
198         assertTrue(cursorWindow.putString(Long.toString(NUMBER_LONG_INTEGER), 0, 0));
199         assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 0));
200         assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 0));
201         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 0));
202         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 0));
203         // Converting of Float, there would be some little precision differences. So just compare
204         // first 6 digits.
205         assertEquals(NUMBER_FLOAT_SCIENCE_STRING.substring(0, 6), Float.toString(
206                 cursorWindow.getFloat(0, 0)).substring(0, 6));
207         assertEquals(NUMBER_DOUBLE_SCIENCE_STRING, Double.toString(cursorWindow.getDouble(0, 0)));
208         assertFalse(cursorWindow.isNull(0, 0));
209         assertFalse(cursorWindow.isBlob(0, 0));
210 
211         // Test null String
212         assertTrue(cursorWindow.putString("", 0, 0));
213         assertEquals("", cursorWindow.getString(0, 0));
214         assertEquals(0, cursorWindow.getLong(0, 0));
215         assertEquals(0, cursorWindow.getInt(0, 0));
216         assertEquals(0, cursorWindow.getShort(0, 0));
217         assertEquals(0.0, cursorWindow.getDouble(0, 0));
218         assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.00000001f);
219         assertFalse(cursorWindow.isNull(0, 0));
220         assertFalse(cursorWindow.isBlob(0, 0));
221 
222         // Test putNull, getString, getLong, getDouble, getBlob, getInd, getShort, getFloat,
223         // isBlob.
224         assertTrue(cursorWindow.putNull(0, 1));
225         assertNull(cursorWindow.getString(0, 1));
226         assertEquals(0, cursorWindow.getLong(0, 1));
227         assertEquals(0, cursorWindow.getInt(0, 1));
228         assertEquals(0, cursorWindow.getShort(0, 1));
229         assertEquals(0.0, cursorWindow.getDouble(0, 1));
230         assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.00000001f);
231         assertNull(cursorWindow.getBlob(0, 1));
232         assertTrue(cursorWindow.isNull(0, 1));
233         // If the field is null, isBlob will return true.
234         assertTrue(cursorWindow.isBlob(0, 1));
235 
236         // Test putLong, getLong, getInt, getString , getShort, getFloat, getDouble, isBlob.
237         assertTrue(cursorWindow.putLong(NUMBER_LONG_INTEGER, 0, 2));
238         assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 2));
239         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2));
240         assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2));
241         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2));
242         assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.00000001f);
243         assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.00000001);
244         try {
245             cursorWindow.getBlob(0, 2);
246             fail("Can't get Blob from a Integer value.");
247         } catch (SQLiteException e) {
248             // expected
249         }
250         assertFalse(cursorWindow.isNull(0, 2));
251         assertFalse(cursorWindow.isBlob(0, 2));
252 
253         // Test putDouble
254         assertTrue(cursorWindow.putDouble(NUMBER_DOUBLE_SCIENCE, 0, 3));
255         assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 3));
256         assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 3));
257         // Converting from Double to String, there would be some little precision differences. So
258         // Just compare first 6 digits.
259         assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3)
260                 .substring(0, 6));
261         assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3));
262         assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.00000001f);
263         assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.00000001);
264         try {
265             cursorWindow.getBlob(0, 3);
266             fail("Can't get Blob from a Double value.");
267         } catch (SQLiteException e) {
268             // expected
269         }
270         assertFalse(cursorWindow.isNull(0, 3));
271         assertFalse(cursorWindow.isBlob(0, 3));
272 
273         // Test putBlob
274         assertTrue(cursorWindow.putBlob(originalBlob, 0, 4));
275         byte[] targetBlob = cursorWindow.getBlob(0, 4);
276         assertTrue(Arrays.equals(originalBlob, targetBlob));
277         assertFalse(cursorWindow.isNull(0, 4));
278         // Test isBlob
279         assertTrue(cursorWindow.isBlob(0, 4));
280     }
281 
testCopyStringToBuffer()282     public void testCopyStringToBuffer() {
283         int DEFAULT_ARRAY_LENGTH = 64;
284         String baseString = "0123456789";
285         String expectedString = "";
286         // Create a 60 characters string.
287         for (int i = 0; i < 6; i++) {
288             expectedString += baseString;
289         }
290         CharArrayBuffer charArrayBuffer = new CharArrayBuffer(null);
291         CursorWindow cursorWindow = new CursorWindow(true);
292         cursorWindow.setNumColumns(2);
293         cursorWindow.allocRow();
294 
295         assertEquals(null, charArrayBuffer.data);
296         cursorWindow.putString(expectedString, 0, 0);
297         cursorWindow.copyStringToBuffer(0, 0, charArrayBuffer);
298         assertNotNull(charArrayBuffer.data);
299         // By default, if the field's string is shorter than 64, array will be allocated as 64.
300         assertEquals(DEFAULT_ARRAY_LENGTH, charArrayBuffer.data.length);
301         assertEquals(expectedString,
302                 new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
303 
304         // Test in case of string is longer than 64,
305         expectedString += baseString;
306         charArrayBuffer = new CharArrayBuffer(null);
307         cursorWindow.putString(expectedString, 0, 1);
308         cursorWindow.copyStringToBuffer(0, 1, charArrayBuffer);
309         assertNotNull(charArrayBuffer.data);
310         // If the string is longer than 64, array will be allocated as needed(longer than 64).
311         assertEquals(expectedString,
312                 new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
313         assertEquals(70, expectedString.length());
314         assertEquals(expectedString.length(), charArrayBuffer.data.length);
315     }
316 
testAccessStartPosition()317     public void testAccessStartPosition() {
318         final int TEST_POSITION_1 = 0;
319         final int TEST_POSITION_2 = 3;
320 
321         CursorWindow cursorWindow = new CursorWindow(true);
322         fillCursorTestContents(cursorWindow, 5);
323 
324         // Test setStartPosition
325         assertEquals(TEST_POSITION_1, cursorWindow.getStartPosition());
326         assertEquals(3, cursorWindow.getInt(3, 0));
327         assertEquals(TEST_STRING + "3", cursorWindow.getString(3, 1));
328         assertEquals(4, cursorWindow.getInt(4, 0));
329         assertEquals(TEST_STRING + "4", cursorWindow.getString(4, 1));
330         cursorWindow.setStartPosition(TEST_POSITION_2);
331 
332         assertEquals(TEST_POSITION_2, cursorWindow.getStartPosition());
333 
334         assertEquals(0, cursorWindow.getInt(3, 0));
335         assertEquals(TEST_STRING + "0", cursorWindow.getString(3, 1));
336         assertEquals(1, cursorWindow.getInt(4, 0));
337         assertEquals(TEST_STRING + "1", cursorWindow.getString(4, 1));
338         try {
339             cursorWindow.getBlob(0, 0);
340             fail("Row number is smaller than startPosition, will cause a IllegalStateException.");
341         } catch (IllegalStateException e) {
342             // expected
343         }
344     }
345 
testClearAndOnAllReferencesReleased()346     public void testClearAndOnAllReferencesReleased() {
347         MockCursorWindow cursorWindow = new MockCursorWindow(true);
348 
349         assertEquals(0, cursorWindow.getNumRows());
350         fillCursorTestContents(cursorWindow, 10);
351         assertEquals(10, cursorWindow.getNumRows());
352         assertEquals(0, cursorWindow.getStartPosition());
353         cursorWindow.setStartPosition(5);
354         assertEquals(5, cursorWindow.getStartPosition());
355 
356         // Test clear(). a complete calling process of cursorWindow has a perfect acquiring and
357         // releasing pair, so the references number will be equal at the begin and the end.
358         assertFalse(cursorWindow.hasReleasedAllReferences());
359         cursorWindow.clear();
360         assertEquals(0, cursorWindow.getNumRows());
361         assertEquals(0, cursorWindow.getStartPosition());
362         assertFalse(cursorWindow.hasReleasedAllReferences());
363 
364         // Test onAllReferencesReleased.
365         // By default, cursorWindow's reference is 1, when it reachs 0, onAllReferencesReleased
366         // be invoked.
367         cursorWindow = new MockCursorWindow(true);
368         cursorWindow.releaseReference();
369         assertTrue(cursorWindow.hasReleasedAllReferences());
370     }
371 
testDescribeContents()372     public void testDescribeContents() {
373         CursorWindow cursorWindow = new CursorWindow(true);
374         assertEquals(0, cursorWindow.describeContents());
375     }
376 
377     private class MockCursorWindow extends CursorWindow {
378         private boolean mHasReleasedAllReferences = false;
379 
MockCursorWindow(boolean localWindow)380         public MockCursorWindow(boolean localWindow) {
381             super(localWindow);
382         }
383 
384         @Override
onAllReferencesReleased()385         protected void onAllReferencesReleased() {
386             super.onAllReferencesReleased();
387             mHasReleasedAllReferences = true;
388         }
389 
hasReleasedAllReferences()390         public boolean hasReleasedAllReferences() {
391             return mHasReleasedAllReferences;
392         }
393 
resetStatus()394         public void resetStatus() {
395             mHasReleasedAllReferences = false;
396         }
397     }
398 
fillCursorTestContents(CursorWindow cursorWindow, int length)399     private void fillCursorTestContents(CursorWindow cursorWindow, int length) {
400         cursorWindow.clear();
401         cursorWindow.setStartPosition(0);
402         cursorWindow.setNumColumns(2);
403         for (int i = 0; i < length; i++) {
404             cursorWindow.allocRow();
405             cursorWindow.putLong(i, i, 0);
406             cursorWindow.putString(TEST_STRING + i, i, 1);
407         }
408     }
409 
createTestList(int rows, int cols)410     private static ArrayList<ArrayList<Integer>> createTestList(int rows, int cols) {
411         ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
412         Random generator = new Random();
413 
414         for (int i = 0; i < rows; i++) {
415             ArrayList<Integer> col = new ArrayList<Integer>();
416             list.add(col);
417             for (int j = 0; j < cols; j++) {
418                 // generate random number
419                 col.add(j == 0 ? i : generator.nextInt());
420             }
421         }
422         return list;
423     }
424 
425     /**
426      * The method comes from unit_test CursorWindowTest.
427      */
getOneByOneWindow()428     private CursorWindow getOneByOneWindow() {
429         CursorWindow window = new CursorWindow(false);
430         assertTrue(window.setNumColumns(1));
431         assertTrue(window.allocRow());
432         return window;
433     }
434 }
435