| /* |
| * Copyright (C) 2009 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.content; |
| |
| import android.database.Cursor; |
| import android.database.MatrixCursor; |
| import android.net.Uri; |
| import android.os.Parcel; |
| import android.text.TextUtils; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import junit.framework.TestCase; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| @SmallTest |
| public class ContentProviderOperationTest extends TestCase { |
| private final static Uri sTestUri1 = Uri.parse("content://authority/blah"); |
| private final static ContentValues sTestValues1; |
| |
| private final static Class<ContentProviderOperation.Builder> CLASS_BUILDER = |
| ContentProviderOperation.Builder.class; |
| private final static Class<ContentProviderOperation> CLASS_OPERATION = |
| ContentProviderOperation.class; |
| |
| static { |
| sTestValues1 = new ContentValues(); |
| sTestValues1.put("a", 1); |
| sTestValues1.put("b", "two"); |
| } |
| |
| public void testInsert() throws OperationApplicationException { |
| ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1) |
| .withValues(sTestValues1) |
| .build(); |
| ContentProviderResult result = op1.apply(new TestContentProvider() { |
| public Uri insert(Uri uri, ContentValues values) { |
| assertEquals(sTestUri1.toString(), uri.toString()); |
| assertEquals(sTestValues1.toString(), values.toString()); |
| return uri.buildUpon().appendPath("19").build(); |
| } |
| }, null, 0); |
| assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString()); |
| } |
| |
| public void testInsertNoValues() throws OperationApplicationException { |
| ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1) |
| .build(); |
| ContentProviderResult result = op1.apply(new TestContentProvider() { |
| public Uri insert(Uri uri, ContentValues values) { |
| assertEquals(sTestUri1.toString(), uri.toString()); |
| assertNull(values); |
| return uri.buildUpon().appendPath("19").build(); |
| } |
| }, null, 0); |
| assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString()); |
| } |
| |
| public void testInsertFailed() { |
| ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1) |
| .withValues(sTestValues1) |
| .build(); |
| try { |
| op1.apply(new TestContentProvider() { |
| public Uri insert(Uri uri, ContentValues values) { |
| assertEquals(sTestUri1.toString(), uri.toString()); |
| assertEquals(sTestValues1.toString(), values.toString()); |
| return null; |
| } |
| }, null, 0); |
| fail("the apply should have thrown an OperationApplicationException"); |
| } catch (OperationApplicationException e) { |
| // this is the expected case |
| } |
| } |
| |
| public void testInsertWithBackRefs() throws OperationApplicationException { |
| ContentProviderResult[] previousResults = new ContentProviderResult[4]; |
| previousResults[0] = new ContentProviderResult(100); |
| previousResults[1] = new ContentProviderResult(101); |
| previousResults[2] = new ContentProviderResult(102); |
| previousResults[3] = new ContentProviderResult(103); |
| ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1) |
| .withValues(sTestValues1) |
| .withValueBackReference("a1", 3) |
| .withValueBackReference("a2", 1) |
| .build(); |
| ContentProviderResult result = op1.apply(new TestContentProvider() { |
| public Uri insert(Uri uri, ContentValues values) { |
| assertEquals(sTestUri1.toString(), uri.toString()); |
| ContentValues expected = new ContentValues(sTestValues1); |
| expected.put("a1", 103); |
| expected.put("a2", 101); |
| assertEquals(expected.toString(), values.toString()); |
| return uri.buildUpon().appendPath("19").build(); |
| } |
| }, previousResults, previousResults.length); |
| assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString()); |
| } |
| |
| public void testUpdate() throws OperationApplicationException { |
| ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1) |
| .withValues(sTestValues1) |
| .build(); |
| ContentProviderResult[] backRefs = new ContentProviderResult[2]; |
| ContentProviderResult result = op1.apply(new TestContentProvider() { |
| public Uri insert(Uri uri, ContentValues values) { |
| assertEquals(sTestUri1.toString(), uri.toString()); |
| assertEquals(sTestValues1.toString(), values.toString()); |
| return uri.buildUpon().appendPath("19").build(); |
| } |
| }, backRefs, 1); |
| assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString()); |
| } |
| |
| public void testAssert() { |
| // Build an operation to assert values match provider |
| ContentProviderOperation op1 = ContentProviderOperation.newAssertQuery(sTestUri1) |
| .withValues(sTestValues1).build(); |
| |
| try { |
| // Assert that values match from cursor |
| ContentProviderResult result = op1.apply(new TestContentProvider() { |
| public Cursor query(Uri uri, String[] projection, String selection, |
| String[] selectionArgs, String sortOrder) { |
| // Return cursor over specific set of values |
| return getCursor(sTestValues1, 1); |
| } |
| }, null, 0); |
| } catch (OperationApplicationException e) { |
| fail("newAssert() failed"); |
| } |
| } |
| |
| public void testAssertNoValues() { |
| // Build an operation to assert values match provider |
| ContentProviderOperation op1 = ContentProviderOperation.newAssertQuery(sTestUri1) |
| .withExpectedCount(1).build(); |
| |
| try { |
| // Assert that values match from cursor |
| ContentProviderResult result = op1.apply(new TestContentProvider() { |
| public Cursor query(Uri uri, String[] projection, String selection, |
| String[] selectionArgs, String sortOrder) { |
| // Return cursor over specific set of values |
| return getCursor(sTestValues1, 1); |
| } |
| }, null, 0); |
| } catch (OperationApplicationException e) { |
| fail("newAssert() failed"); |
| } |
| |
| ContentProviderOperation op2 = ContentProviderOperation.newAssertQuery(sTestUri1) |
| .withExpectedCount(0).build(); |
| |
| try { |
| // Assert that values match from cursor |
| ContentProviderResult result = op2.apply(new TestContentProvider() { |
| public Cursor query(Uri uri, String[] projection, String selection, |
| String[] selectionArgs, String sortOrder) { |
| // Return cursor over specific set of values |
| return getCursor(sTestValues1, 0); |
| } |
| }, null, 0); |
| } catch (OperationApplicationException e) { |
| fail("newAssert() failed"); |
| } |
| |
| ContentProviderOperation op3 = ContentProviderOperation.newAssertQuery(sTestUri1) |
| .withExpectedCount(2).build(); |
| |
| try { |
| // Assert that values match from cursor |
| ContentProviderResult result = op3.apply(new TestContentProvider() { |
| public Cursor query(Uri uri, String[] projection, String selection, |
| String[] selectionArgs, String sortOrder) { |
| // Return cursor over specific set of values |
| return getCursor(sTestValues1, 5); |
| } |
| }, null, 0); |
| fail("we expect the exception to be thrown"); |
| } catch (OperationApplicationException e) { |
| } |
| } |
| |
| /** |
| * Build a {@link Cursor} with a single row that contains all values |
| * provided through the given {@link ContentValues}. |
| */ |
| private Cursor getCursor(ContentValues contentValues, int numRows) { |
| final Set<Entry<String, Object>> valueSet = contentValues.valueSet(); |
| final String[] keys = new String[valueSet.size()]; |
| final Object[] values = new Object[valueSet.size()]; |
| |
| int i = 0; |
| for (Entry<String, Object> entry : valueSet) { |
| keys[i] = entry.getKey(); |
| values[i] = entry.getValue(); |
| i++; |
| } |
| |
| final MatrixCursor cursor = new MatrixCursor(keys); |
| for (i = 0; i < numRows; i++) { |
| cursor.addRow(values); |
| } |
| return cursor; |
| } |
| |
| public void testValueBackRefs() { |
| ContentValues values = new ContentValues(); |
| values.put("a", "in1"); |
| values.put("a2", "in2"); |
| values.put("b", "in3"); |
| values.put("c", "in4"); |
| |
| ContentProviderResult[] previousResults = new ContentProviderResult[4]; |
| previousResults[0] = new ContentProviderResult(100); |
| previousResults[1] = new ContentProviderResult(101); |
| previousResults[2] = new ContentProviderResult(102); |
| previousResults[3] = new ContentProviderResult(103); |
| |
| ContentValues expectedValues = new ContentValues(values); |
| expectedValues.put("a1", (long) 103); |
| expectedValues.put("a2", (long) 101); |
| expectedValues.put("a3", (long) 102); |
| |
| ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1) |
| .withValues(values) |
| .withValueBackReference("a1", 3) |
| .withValueBackReference("a2", 1) |
| .withValueBackReference("a3", 2) |
| .build(); |
| ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length); |
| assertEquals(expectedValues, v2); |
| } |
| |
| public void testSelectionBackRefs() { |
| ContentProviderResult[] previousResults = new ContentProviderResult[4]; |
| previousResults[0] = new ContentProviderResult(100); |
| previousResults[1] = new ContentProviderResult(101); |
| previousResults[2] = new ContentProviderResult(102); |
| previousResults[3] = new ContentProviderResult(103); |
| |
| String[] selectionArgs = new String[]{"a", null, null, "b", null}; |
| |
| final ContentValues values = new ContentValues(); |
| values.put("unused", "unused"); |
| |
| ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1) |
| .withSelectionBackReference(1, 3) |
| .withSelectionBackReference(2, 1) |
| .withSelectionBackReference(4, 2) |
| .withSelection("unused", selectionArgs) |
| .withValues(values) |
| .build(); |
| String[] s2 = op1.resolveSelectionArgsBackReferences( |
| previousResults, previousResults.length); |
| assertEquals("a,103,101,b,102", TextUtils.join(",", s2)); |
| } |
| |
| public void testParcelingOperation() throws NoSuchFieldException, IllegalAccessException, |
| NoSuchMethodException, InvocationTargetException, InstantiationException { |
| Parcel parcel = Parcel.obtain(); |
| ContentProviderOperation op1; |
| ContentProviderOperation op2; |
| |
| HashMap<Integer, Integer> selArgsBackRef = new HashMap<Integer, Integer>(); |
| selArgsBackRef.put(1, 2); |
| selArgsBackRef.put(3, 4); |
| |
| ContentValues values = new ContentValues(); |
| values.put("v1", "val1"); |
| values.put("v2", "43"); |
| |
| ContentValues valuesBackRef = new ContentValues(); |
| values.put("v3", "val3"); |
| values.put("v4", "44"); |
| |
| try { |
| ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert( |
| Uri.parse("content://goo/bar")); |
| |
| builderSetExpectedCount(builder, 42); |
| builderSetSelection(builder, "selection"); |
| builderSetSelectionArgs(builder, new String[]{"a", "b"}); |
| builderSetSelectionArgsBackReferences(builder, selArgsBackRef); |
| builderSetValues(builder, values); |
| builderSetValuesBackReferences(builder, valuesBackRef); |
| |
| op1 = newOperationFromBuilder(builder); |
| op1.writeToParcel(parcel, 0); |
| parcel.setDataPosition(0); |
| op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel); |
| |
| assertEquals(ContentProviderOperation.TYPE_INSERT, operationGetType(op2)); |
| assertEquals("content://goo/bar", operationGetUri(op2).toString()); |
| assertEquals(Integer.valueOf(42), operationGetExpectedCount(op2)); |
| assertEquals("selection", operationGetSelection(op2)); |
| assertEquals(2, operationGetSelectionArgs(op2).length); |
| assertEquals("a", operationGetSelectionArgs(op2)[0]); |
| assertEquals("b", operationGetSelectionArgs(op2)[1]); |
| assertEquals(values, operationGetValues(op2)); |
| assertEquals(valuesBackRef, operationGetValuesBackReferences(op2)); |
| assertEquals(2, operationGetSelectionArgsBackReferences(op2).size()); |
| assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1)); |
| assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3)); |
| } finally { |
| parcel.recycle(); |
| } |
| |
| try { |
| ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate( |
| Uri.parse("content://goo/bar")); |
| |
| builderSetSelectionArgsBackReferences(builder, selArgsBackRef); |
| |
| op1 = newOperationFromBuilder(builder); |
| op1.writeToParcel(parcel, 0); |
| parcel.setDataPosition(0); |
| op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel); |
| assertEquals(ContentProviderOperation.TYPE_UPDATE, operationGetType(op2)); |
| assertEquals("content://goo/bar", operationGetUri(op2).toString()); |
| assertNull(operationGetExpectedCount(op2)); |
| assertNull(operationGetSelection(op2)); |
| assertNull(operationGetSelectionArgs(op2)); |
| assertNull(operationGetValues(op2)); |
| assertNull(operationGetValuesBackReferences(op2)); |
| assertEquals(2, operationGetSelectionArgsBackReferences(op2).size()); |
| assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1)); |
| assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3)); |
| } finally { |
| parcel.recycle(); |
| } |
| |
| try { |
| ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete( |
| Uri.parse("content://goo/bar")); |
| |
| op1 = newOperationFromBuilder(builder); |
| op1.writeToParcel(parcel, 0); |
| parcel.setDataPosition(0); |
| op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel); |
| assertEquals(ContentProviderOperation.TYPE_DELETE, operationGetType(op2)); |
| assertEquals("content://goo/bar", operationGetUri(op2).toString()); |
| assertNull(operationGetExpectedCount(op2)); |
| assertNull(operationGetSelection(op2)); |
| assertNull(operationGetSelectionArgs(op2)); |
| assertNull(operationGetValues(op2)); |
| assertNull(operationGetValuesBackReferences(op2)); |
| assertNull(operationGetSelectionArgsBackReferences(op2)); |
| } finally { |
| parcel.recycle(); |
| } |
| } |
| |
| private static ContentProviderOperation newOperationFromBuilder( |
| ContentProviderOperation.Builder builder) |
| throws NoSuchMethodException, InstantiationException, IllegalAccessException, |
| InvocationTargetException { |
| final Constructor constructor = CLASS_OPERATION.getDeclaredConstructor(CLASS_BUILDER); |
| constructor.setAccessible(true); |
| return (ContentProviderOperation) constructor.newInstance(builder); |
| } |
| |
| private void builderSetSelectionArgsBackReferences( |
| ContentProviderOperation.Builder builder, HashMap<Integer, Integer> selArgsBackRef) |
| throws NoSuchFieldException, IllegalAccessException { |
| Field field; |
| field = CLASS_BUILDER.getDeclaredField("mSelectionArgsBackReferences"); |
| field.setAccessible(true); |
| field.set(builder, selArgsBackRef); |
| } |
| |
| private void builderSetValuesBackReferences( |
| ContentProviderOperation.Builder builder, ContentValues valuesBackReferences) |
| throws NoSuchFieldException, IllegalAccessException { |
| Field field; |
| field = CLASS_BUILDER.getDeclaredField("mValuesBackReferences"); |
| field.setAccessible(true); |
| field.set(builder, valuesBackReferences); |
| } |
| |
| private void builderSetSelection( |
| ContentProviderOperation.Builder builder, String selection) |
| throws NoSuchFieldException, IllegalAccessException { |
| Field field; |
| field = CLASS_BUILDER.getDeclaredField("mSelection"); |
| field.setAccessible(true); |
| field.set(builder, selection); |
| } |
| |
| private void builderSetSelectionArgs( |
| ContentProviderOperation.Builder builder, String[] selArgs) |
| throws NoSuchFieldException, IllegalAccessException { |
| Field field; |
| field = CLASS_BUILDER.getDeclaredField("mSelectionArgs"); |
| field.setAccessible(true); |
| field.set(builder, selArgs); |
| } |
| |
| private void builderSetValues( |
| ContentProviderOperation.Builder builder, ContentValues values) |
| throws NoSuchFieldException, IllegalAccessException { |
| Field field; |
| field = CLASS_BUILDER.getDeclaredField("mValues"); |
| field.setAccessible(true); |
| field.set(builder, values); |
| } |
| |
| private void builderSetExpectedCount( |
| ContentProviderOperation.Builder builder, Integer expectedCount) |
| throws NoSuchFieldException, IllegalAccessException { |
| Field field; |
| field = CLASS_BUILDER.getDeclaredField("mExpectedCount"); |
| field.setAccessible(true); |
| field.set(builder, expectedCount); |
| } |
| |
| private int operationGetType(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mType"); |
| field.setAccessible(true); |
| return field.getInt(operation); |
| } |
| |
| private Uri operationGetUri(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mUri"); |
| field.setAccessible(true); |
| return (Uri) field.get(operation); |
| } |
| |
| private String operationGetSelection(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mSelection"); |
| field.setAccessible(true); |
| return (String) field.get(operation); |
| } |
| |
| private String[] operationGetSelectionArgs(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgs"); |
| field.setAccessible(true); |
| return (String[]) field.get(operation); |
| } |
| |
| private ContentValues operationGetValues(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mValues"); |
| field.setAccessible(true); |
| return (ContentValues) field.get(operation); |
| } |
| |
| private Integer operationGetExpectedCount(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mExpectedCount"); |
| field.setAccessible(true); |
| return (Integer) field.get(operation); |
| } |
| |
| private ContentValues operationGetValuesBackReferences(ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mValuesBackReferences"); |
| field.setAccessible(true); |
| return (ContentValues) field.get(operation); |
| } |
| |
| private Map<Integer, Integer> operationGetSelectionArgsBackReferences( |
| ContentProviderOperation operation) |
| throws NoSuchFieldException, IllegalAccessException { |
| final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgsBackReferences"); |
| field.setAccessible(true); |
| return (Map<Integer, Integer>) field.get(operation); |
| } |
| |
| public void testParcelingResult() { |
| Parcel parcel = Parcel.obtain(); |
| ContentProviderResult result1; |
| ContentProviderResult result2; |
| try { |
| result1 = new ContentProviderResult(Uri.parse("content://goo/bar")); |
| result1.writeToParcel(parcel, 0); |
| parcel.setDataPosition(0); |
| result2 = ContentProviderResult.CREATOR.createFromParcel(parcel); |
| assertEquals("content://goo/bar", result2.uri.toString()); |
| assertNull(result2.count); |
| } finally { |
| parcel.recycle(); |
| } |
| |
| parcel = Parcel.obtain(); |
| try { |
| result1 = new ContentProviderResult(42); |
| result1.writeToParcel(parcel, 0); |
| parcel.setDataPosition(0); |
| result2 = ContentProviderResult.CREATOR.createFromParcel(parcel); |
| assertEquals(Integer.valueOf(42), result2.count); |
| assertNull(result2.uri); |
| } finally { |
| parcel.recycle(); |
| } |
| } |
| |
| static class TestContentProvider extends ContentProvider { |
| public boolean onCreate() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, |
| String sortOrder) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String getType(Uri uri) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Uri insert(Uri uri, ContentValues values) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public int delete(Uri uri, String selection, String[] selectionArgs) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |