| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.database.cts; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import android.database.CharArrayBuffer; |
| import android.database.CursorWindow; |
| import android.database.MatrixCursor; |
| import android.database.sqlite.SQLiteException; |
| import android.os.Parcel; |
| import android.test.suitebuilder.annotation.SmallTest; |
| |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Random; |
| |
| @RunWith(AndroidJUnit4.class) |
| @SmallTest |
| public class CursorWindowTest { |
| |
| private static final String TEST_STRING = "Test String"; |
| |
| @Test |
| public void testWriteCursorToWindow() throws Exception { |
| // create cursor |
| String[] colNames = new String[]{"_id", "name", "number", "profit"}; |
| int colsize = colNames.length; |
| ArrayList<ArrayList<Integer>> list = createTestList(10, colsize); |
| MatrixCursor cursor = new MatrixCursor(colNames, list.size()); |
| for (ArrayList<Integer> row : list) { |
| cursor.addRow(row); |
| } |
| |
| // fill window |
| CursorWindow window = new CursorWindow(false); |
| cursor.fillWindow(0, window); |
| |
| // read from cursor window |
| for (int i = 0; i < list.size(); i++) { |
| ArrayList<Integer> col = list.get(i); |
| for (int j = 0; j < colsize; j++) { |
| String s = window.getString(i, j); |
| int r2 = col.get(j); |
| int r1 = Integer.parseInt(s); |
| assertEquals(r2, r1); |
| } |
| } |
| |
| // test cursor window handle startpos != 0 |
| window.clear(); |
| cursor.fillWindow(1, window); |
| // read from cursor from window |
| for (int i = 1; i < list.size(); i++) { |
| ArrayList<Integer> col = list.get(i); |
| for (int j = 0; j < colsize; j++) { |
| String s = window.getString(i, j); |
| int r2 = col.get(j); |
| int r1 = Integer.parseInt(s); |
| assertEquals(r2, r1); |
| } |
| } |
| |
| // Clear the window and make sure it's empty |
| window.clear(); |
| assertEquals(0, window.getNumRows()); |
| } |
| |
| @Test |
| public void testNull() { |
| CursorWindow window = getOneByOneWindow(); |
| |
| // Put in a null value and read it back as various types |
| assertTrue(window.putNull(0, 0)); |
| assertNull(window.getString(0, 0)); |
| assertEquals(0, window.getLong(0, 0)); |
| assertEquals(0.0, window.getDouble(0, 0), 0.0); |
| assertNull(window.getBlob(0, 0)); |
| } |
| |
| @Test |
| public void testEmptyString() { |
| CursorWindow window = getOneByOneWindow(); |
| |
| // put size 0 string and read it back as various types |
| assertTrue(window.putString("", 0, 0)); |
| assertEquals("", window.getString(0, 0)); |
| assertEquals(0, window.getLong(0, 0)); |
| assertEquals(0.0, window.getDouble(0, 0), 0.0); |
| } |
| |
| @Test |
| public void testConstructors() { |
| int TEST_NUMBER = 5; |
| CursorWindow cursorWindow; |
| |
| // Test constructor with 'true' input, and getStartPosition should return 0 |
| cursorWindow = new CursorWindow(true); |
| assertEquals(0, cursorWindow.getStartPosition()); |
| |
| // Test constructor with 'false' input |
| cursorWindow = new CursorWindow(false); |
| assertEquals(0, cursorWindow.getStartPosition()); |
| |
| // Test newFromParcel |
| Parcel parcel = Parcel.obtain(); |
| cursorWindow = new CursorWindow(true); |
| cursorWindow.setStartPosition(TEST_NUMBER); |
| cursorWindow.setNumColumns(1); |
| cursorWindow.allocRow(); |
| cursorWindow.putString(TEST_STRING, TEST_NUMBER, 0); |
| cursorWindow.writeToParcel(parcel, 0); |
| |
| parcel.setDataPosition(0); |
| cursorWindow = CursorWindow.CREATOR.createFromParcel(parcel); |
| assertEquals(TEST_NUMBER, cursorWindow.getStartPosition()); |
| assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0)); |
| |
| parcel.recycle(); |
| } |
| |
| @Test |
| public void testDataStructureOperations() { |
| CursorWindow cursorWindow = new CursorWindow(true); |
| |
| // Test with normal values |
| assertTrue(cursorWindow.setNumColumns(0)); |
| // If the column has been set to zero, can't put String. |
| assertFalse(cursorWindow.putString(TEST_STRING, 0, 0)); |
| |
| // Test allocRow(). |
| assertTrue(cursorWindow.allocRow()); |
| assertEquals(1, cursorWindow.getNumRows()); |
| assertTrue(cursorWindow.allocRow()); |
| assertEquals(2, cursorWindow.getNumRows()); |
| // Though allocate a row, but the column number is still 0, so can't putString. |
| assertFalse(cursorWindow.putString(TEST_STRING, 0, 0)); |
| |
| // Test freeLstRow |
| cursorWindow.freeLastRow(); |
| assertEquals(1, cursorWindow.getNumRows()); |
| cursorWindow.freeLastRow(); |
| assertEquals(0, cursorWindow.getNumRows()); |
| |
| cursorWindow = new CursorWindow(true); |
| assertTrue(cursorWindow.setNumColumns(6)); |
| assertTrue(cursorWindow.allocRow()); |
| // Column number set to negative number, so now can put values. |
| assertTrue(cursorWindow.putString(TEST_STRING, 0, 0)); |
| assertEquals(TEST_STRING, cursorWindow.getString(0, 0)); |
| |
| // Test with negative value |
| assertFalse(cursorWindow.setNumColumns(-1)); |
| |
| // Test with reference limitation |
| cursorWindow.releaseReference(); |
| try { |
| cursorWindow.setNumColumns(5); |
| fail("setNumColumns() should throws IllegalStateException here."); |
| } catch (IllegalStateException e) { |
| // expected |
| } |
| |
| // Test close(), close will also minus references, that will lead acquireReference() |
| // related operation failed. |
| cursorWindow.close(); |
| try { |
| cursorWindow.acquireReference(); |
| fail("setNumColumns() should throws IllegalStateException here."); |
| } catch (IllegalStateException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void testAccessDataValues() { |
| final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL; |
| final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER; |
| final long NUMBER_SHORT = (short) NUMBER_INTEGER; |
| final float NUMBER_FLOAT_SCIENCE = 7.332952E11f; |
| final double NUMBER_DOUBLE_SCIENCE = 7.33295205887E11; |
| final String NUMBER_FLOAT_SCIENCE_STRING = "7.332952E11"; |
| final String NUMBER_DOUBLE_SCIENCE_STRING = "7.33295205887E11"; |
| final String NUMBER_FLOAT_SCIENCE_STRING2 = "7.33295e+11"; |
| |
| byte[] originalBlob = new byte[Byte.MAX_VALUE]; |
| for (int i = 0; i < Byte.MAX_VALUE; i++) { |
| originalBlob[i] = (byte) i; |
| } |
| |
| CursorWindow cursorWindow = new CursorWindow(true); |
| cursorWindow.setNumColumns(5); |
| cursorWindow.allocRow(); |
| |
| // Test putString, getString, getLong, getInt, isBlob |
| assertTrue(cursorWindow.putString(Long.toString(NUMBER_LONG_INTEGER), 0, 0)); |
| assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 0)); |
| assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 0)); |
| assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 0)); |
| assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 0)); |
| // Converting of Float, there would be some little precision differences. So just compare |
| // first 6 digits. |
| assertEquals(NUMBER_FLOAT_SCIENCE_STRING.substring(0, 6), Float.toString( |
| cursorWindow.getFloat(0, 0)).substring(0, 6)); |
| assertEquals(NUMBER_DOUBLE_SCIENCE_STRING, Double.toString(cursorWindow.getDouble(0, 0))); |
| assertFalse(cursorWindow.isNull(0, 0)); |
| assertFalse(cursorWindow.isBlob(0, 0)); |
| |
| // Test null String |
| assertTrue(cursorWindow.putString("", 0, 0)); |
| assertEquals("", cursorWindow.getString(0, 0)); |
| assertEquals(0, cursorWindow.getLong(0, 0)); |
| assertEquals(0, cursorWindow.getInt(0, 0)); |
| assertEquals(0, cursorWindow.getShort(0, 0)); |
| assertEquals(0.0, cursorWindow.getDouble(0, 0), 0.0); |
| assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.0); |
| assertFalse(cursorWindow.isNull(0, 0)); |
| assertFalse(cursorWindow.isBlob(0, 0)); |
| |
| // Test putNull, getString, getLong, getDouble, getBlob, getInd, getShort, getFloat, |
| // isBlob. |
| assertTrue(cursorWindow.putNull(0, 1)); |
| assertNull(cursorWindow.getString(0, 1)); |
| assertEquals(0, cursorWindow.getLong(0, 1)); |
| assertEquals(0, cursorWindow.getInt(0, 1)); |
| assertEquals(0, cursorWindow.getShort(0, 1)); |
| assertEquals(0.0, cursorWindow.getDouble(0, 1), 0.0); |
| assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.0); |
| assertNull(cursorWindow.getBlob(0, 1)); |
| assertTrue(cursorWindow.isNull(0, 1)); |
| // If the field is null, isBlob will return true. |
| assertTrue(cursorWindow.isBlob(0, 1)); |
| |
| // Test putLong, getLong, getInt, getString , getShort, getFloat, getDouble, isBlob. |
| assertTrue(cursorWindow.putLong(NUMBER_LONG_INTEGER, 0, 2)); |
| assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 2)); |
| assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2)); |
| assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2)); |
| assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2)); |
| assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.0); |
| assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.0); |
| try { |
| cursorWindow.getBlob(0, 2); |
| fail("Can't get Blob from a Integer value."); |
| } catch (SQLiteException e) { |
| // expected |
| } |
| assertFalse(cursorWindow.isNull(0, 2)); |
| assertFalse(cursorWindow.isBlob(0, 2)); |
| |
| // Test putDouble |
| assertTrue(cursorWindow.putDouble(NUMBER_DOUBLE_SCIENCE, 0, 3)); |
| assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 3)); |
| assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 3)); |
| // Converting from Double to String, there would be some little precision differences. So |
| // Just compare first 6 digits. |
| assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3) |
| .substring(0, 6)); |
| assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3)); |
| assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.0); |
| assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.0); |
| try { |
| cursorWindow.getBlob(0, 3); |
| fail("Can't get Blob from a Double value."); |
| } catch (SQLiteException e) { |
| // expected |
| } |
| assertFalse(cursorWindow.isNull(0, 3)); |
| assertFalse(cursorWindow.isBlob(0, 3)); |
| |
| // Test putBlob |
| assertTrue(cursorWindow.putBlob(originalBlob, 0, 4)); |
| byte[] targetBlob = cursorWindow.getBlob(0, 4); |
| assertTrue(Arrays.equals(originalBlob, targetBlob)); |
| assertFalse(cursorWindow.isNull(0, 4)); |
| // Test isBlob |
| assertTrue(cursorWindow.isBlob(0, 4)); |
| } |
| |
| @Test |
| public void testCopyStringToBuffer() { |
| int DEFAULT_ARRAY_LENGTH = 64; |
| String baseString = "0123456789"; |
| String expectedString = ""; |
| // Create a 60 characters string. |
| for (int i = 0; i < 6; i++) { |
| expectedString += baseString; |
| } |
| CharArrayBuffer charArrayBuffer = new CharArrayBuffer(null); |
| CursorWindow cursorWindow = new CursorWindow(true); |
| cursorWindow.setNumColumns(2); |
| cursorWindow.allocRow(); |
| |
| assertEquals(null, charArrayBuffer.data); |
| cursorWindow.putString(expectedString, 0, 0); |
| cursorWindow.copyStringToBuffer(0, 0, charArrayBuffer); |
| assertNotNull(charArrayBuffer.data); |
| // By default, if the field's string is shorter than 64, array will be allocated as 64. |
| assertEquals(DEFAULT_ARRAY_LENGTH, charArrayBuffer.data.length); |
| assertEquals(expectedString, |
| new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied)); |
| |
| // Test in case of string is longer than 64, |
| expectedString += baseString; |
| charArrayBuffer = new CharArrayBuffer(null); |
| cursorWindow.putString(expectedString, 0, 1); |
| cursorWindow.copyStringToBuffer(0, 1, charArrayBuffer); |
| assertNotNull(charArrayBuffer.data); |
| // If the string is longer than 64, array will be allocated as needed(longer than 64). |
| assertEquals(expectedString, |
| new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied)); |
| assertEquals(70, expectedString.length()); |
| assertEquals(expectedString.length(), charArrayBuffer.data.length); |
| } |
| |
| @Test |
| public void testAccessStartPosition() { |
| final int TEST_POSITION_1 = 0; |
| final int TEST_POSITION_2 = 3; |
| |
| CursorWindow cursorWindow = new CursorWindow(true); |
| fillCursorTestContents(cursorWindow, 5); |
| |
| // Test setStartPosition |
| assertEquals(TEST_POSITION_1, cursorWindow.getStartPosition()); |
| assertEquals(3, cursorWindow.getInt(3, 0)); |
| assertEquals(TEST_STRING + "3", cursorWindow.getString(3, 1)); |
| assertEquals(4, cursorWindow.getInt(4, 0)); |
| assertEquals(TEST_STRING + "4", cursorWindow.getString(4, 1)); |
| cursorWindow.setStartPosition(TEST_POSITION_2); |
| |
| assertEquals(TEST_POSITION_2, cursorWindow.getStartPosition()); |
| |
| assertEquals(0, cursorWindow.getInt(3, 0)); |
| assertEquals(TEST_STRING + "0", cursorWindow.getString(3, 1)); |
| assertEquals(1, cursorWindow.getInt(4, 0)); |
| assertEquals(TEST_STRING + "1", cursorWindow.getString(4, 1)); |
| try { |
| cursorWindow.getBlob(0, 0); |
| fail("Row number is smaller than startPosition, will cause a IllegalStateException."); |
| } catch (IllegalStateException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void testClearAndOnAllReferencesReleased() { |
| MockCursorWindow cursorWindow = new MockCursorWindow(true); |
| |
| assertEquals(0, cursorWindow.getNumRows()); |
| fillCursorTestContents(cursorWindow, 10); |
| assertEquals(10, cursorWindow.getNumRows()); |
| assertEquals(0, cursorWindow.getStartPosition()); |
| cursorWindow.setStartPosition(5); |
| assertEquals(5, cursorWindow.getStartPosition()); |
| |
| // Test clear(). a complete calling process of cursorWindow has a perfect acquiring and |
| // releasing pair, so the references number will be equal at the begin and the end. |
| assertFalse(cursorWindow.hasReleasedAllReferences()); |
| cursorWindow.clear(); |
| assertEquals(0, cursorWindow.getNumRows()); |
| assertEquals(0, cursorWindow.getStartPosition()); |
| assertFalse(cursorWindow.hasReleasedAllReferences()); |
| |
| // Test onAllReferencesReleased. |
| // By default, cursorWindow's reference is 1, when it reachs 0, onAllReferencesReleased |
| // be invoked. |
| cursorWindow = new MockCursorWindow(true); |
| cursorWindow.releaseReference(); |
| assertTrue(cursorWindow.hasReleasedAllReferences()); |
| } |
| |
| @Test |
| public void testDescribeContents() { |
| CursorWindow cursorWindow = new CursorWindow(true); |
| assertEquals(0, cursorWindow.describeContents()); |
| } |
| |
| @Test |
| public void testDefaultCursorWindowSize() { |
| CursorWindow cursorWindow = new CursorWindow("test"); |
| cursorWindow.setNumColumns(1); |
| byte[] bytes = new byte[1024]; |
| Arrays.fill(bytes, (byte) 1); |
| // Ensure that the default is not too small and it's possible to fill CursorWindow |
| // with ~2Mb of data |
| int testRowCount = 2016; |
| for (int i = 0; i < testRowCount; i++) { |
| assertTrue(cursorWindow.allocRow()); |
| assertTrue("Allocation failed for row " + i, cursorWindow.putBlob(bytes, i, 0)); |
| } |
| assertTrue(cursorWindow.allocRow()); |
| assertFalse("Allocation should fail for row " + testRowCount, |
| cursorWindow.putBlob(bytes, testRowCount, 0)); |
| } |
| |
| @Test |
| public void testCustomSize() { |
| // Allocate CursorWindow with max size 10KB and test that restriction is enforced |
| CursorWindow cursorWindow = new CursorWindow("test", 10000); |
| cursorWindow.setNumColumns(1); |
| byte[] bytes = new byte[8000]; |
| Arrays.fill(bytes, (byte) 1); |
| assertTrue(cursorWindow.allocRow()); |
| assertTrue("Allocation of 1 row should succeed", cursorWindow.putBlob(bytes, 0, 0)); |
| assertTrue(cursorWindow.allocRow()); |
| assertFalse("Allocation of 2nd row should fail", cursorWindow.putBlob(bytes, 1, 0)); |
| } |
| |
| private class MockCursorWindow extends CursorWindow { |
| private boolean mHasReleasedAllReferences = false; |
| |
| public MockCursorWindow(boolean localWindow) { |
| super(localWindow); |
| } |
| |
| @Override |
| protected void onAllReferencesReleased() { |
| super.onAllReferencesReleased(); |
| mHasReleasedAllReferences = true; |
| } |
| |
| public boolean hasReleasedAllReferences() { |
| return mHasReleasedAllReferences; |
| } |
| |
| public void resetStatus() { |
| mHasReleasedAllReferences = false; |
| } |
| } |
| |
| private void fillCursorTestContents(CursorWindow cursorWindow, int length) { |
| cursorWindow.clear(); |
| cursorWindow.setStartPosition(0); |
| cursorWindow.setNumColumns(2); |
| for (int i = 0; i < length; i++) { |
| cursorWindow.allocRow(); |
| cursorWindow.putLong(i, i, 0); |
| cursorWindow.putString(TEST_STRING + i, i, 1); |
| } |
| } |
| |
| private static ArrayList<ArrayList<Integer>> createTestList(int rows, int cols) { |
| ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>(); |
| Random generator = new Random(); |
| |
| for (int i = 0; i < rows; i++) { |
| ArrayList<Integer> col = new ArrayList<Integer>(); |
| list.add(col); |
| for (int j = 0; j < cols; j++) { |
| // generate random number |
| col.add(j == 0 ? i : generator.nextInt()); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * The method comes from unit_test CursorWindowTest. |
| */ |
| private CursorWindow getOneByOneWindow() { |
| CursorWindow window = new CursorWindow(false); |
| assertTrue(window.setNumColumns(1)); |
| assertTrue(window.allocRow()); |
| return window; |
| } |
| } |