Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2009 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.content;
     18 
     19 import android.content.ContentValues;
     20 import android.database.Cursor;
     21 import android.database.MatrixCursor;
     22 import android.net.Uri;
     23 import android.os.Parcel;
     24 import android.test.suitebuilder.annotation.SmallTest;
     25 import android.text.TextUtils;
     26 import junit.framework.TestCase;
     27 
     28 import java.lang.reflect.Constructor;
     29 import java.lang.reflect.Field;
     30 import java.lang.reflect.InvocationTargetException;
     31 import java.util.HashMap;
     32 import java.util.Set;
     33 import java.util.Map;
     34 import java.util.Map.Entry;
     35 
     36 @SmallTest
     37 public class ContentProviderOperationTest extends TestCase {
     38     private final static Uri sTestUri1 = Uri.parse("content://authority/blah");
     39     private final static ContentValues sTestValues1;
     40 
     41     private final static Class<ContentProviderOperation.Builder> CLASS_BUILDER =
     42             ContentProviderOperation.Builder.class;
     43     private final static Class<ContentProviderOperation> CLASS_OPERATION =
     44             ContentProviderOperation.class;
     45 
     46     static {
     47         sTestValues1 = new ContentValues();
     48         sTestValues1.put("a", 1);
     49         sTestValues1.put("b", "two");
     50     }
     51 
     52     public void testInsert() throws OperationApplicationException {
     53         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
     54                 .withValues(sTestValues1)
     55                 .build();
     56         ContentProviderResult result = op1.apply(new TestContentProvider() {
     57             public Uri insert(Uri uri, ContentValues values) {
     58                 assertEquals(sTestUri1.toString(), uri.toString());
     59                 assertEquals(sTestValues1.toString(), values.toString());
     60                 return uri.buildUpon().appendPath("19").build();
     61             }
     62         }, null, 0);
     63         assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
     64     }
     65 
     66     public void testInsertNoValues() throws OperationApplicationException {
     67         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
     68                 .build();
     69         ContentProviderResult result = op1.apply(new TestContentProvider() {
     70             public Uri insert(Uri uri, ContentValues values) {
     71                 assertEquals(sTestUri1.toString(), uri.toString());
     72                 assertNull(values);
     73                 return uri.buildUpon().appendPath("19").build();
     74             }
     75         }, null, 0);
     76         assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
     77     }
     78 
     79     public void testInsertFailed() {
     80         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
     81                 .withValues(sTestValues1)
     82                 .build();
     83         try {
     84             op1.apply(new TestContentProvider() {
     85                 public Uri insert(Uri uri, ContentValues values) {
     86                     assertEquals(sTestUri1.toString(), uri.toString());
     87                     assertEquals(sTestValues1.toString(), values.toString());
     88                     return null;
     89                 }
     90             }, null, 0);
     91             fail("the apply should have thrown an OperationApplicationException");
     92         } catch (OperationApplicationException e) {
     93             // this is the expected case
     94         }
     95     }
     96 
     97     public void testInsertWithBackRefs() throws OperationApplicationException {
     98         ContentProviderResult[] previousResults = new ContentProviderResult[4];
     99         previousResults[0] = new ContentProviderResult(100);
    100         previousResults[1] = new ContentProviderResult(101);
    101         previousResults[2] = new ContentProviderResult(102);
    102         previousResults[3] = new ContentProviderResult(103);
    103         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
    104                 .withValues(sTestValues1)
    105                 .withValueBackReference("a1", 3)
    106                 .withValueBackReference("a2", 1)
    107                 .build();
    108         ContentProviderResult result = op1.apply(new TestContentProvider() {
    109             public Uri insert(Uri uri, ContentValues values) {
    110                 assertEquals(sTestUri1.toString(), uri.toString());
    111                 ContentValues expected = new ContentValues(sTestValues1);
    112                 expected.put("a1", 103);
    113                 expected.put("a2", 101);
    114                 assertEquals(expected.toString(), values.toString());
    115                 return uri.buildUpon().appendPath("19").build();
    116             }
    117         }, previousResults, previousResults.length);
    118         assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
    119     }
    120 
    121     public void testUpdate() throws OperationApplicationException {
    122         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
    123                 .withValues(sTestValues1)
    124                 .build();
    125         ContentProviderResult[] backRefs = new ContentProviderResult[2];
    126         ContentProviderResult result = op1.apply(new TestContentProvider() {
    127             public Uri insert(Uri uri, ContentValues values) {
    128                 assertEquals(sTestUri1.toString(), uri.toString());
    129                 assertEquals(sTestValues1.toString(), values.toString());
    130                 return uri.buildUpon().appendPath("19").build();
    131             }
    132         }, backRefs, 1);
    133         assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
    134     }
    135 
    136     public void testAssert() {
    137         // Build an operation to assert values match provider
    138         ContentProviderOperation op1 = ContentProviderOperation.newAssertQuery(sTestUri1)
    139                 .withValues(sTestValues1).build();
    140 
    141         try {
    142             // Assert that values match from cursor
    143             ContentProviderResult result = op1.apply(new TestContentProvider() {
    144                 public Cursor query(Uri uri, String[] projection, String selection,
    145                         String[] selectionArgs, String sortOrder) {
    146                     // Return cursor over specific set of values
    147                     return getCursor(sTestValues1, 1);
    148                 }
    149             }, null, 0);
    150         } catch (OperationApplicationException e) {
    151             fail("newAssert() failed");
    152         }
    153     }
    154 
    155     public void testAssertNoValues() {
    156         // Build an operation to assert values match provider
    157         ContentProviderOperation op1 = ContentProviderOperation.newAssertQuery(sTestUri1)
    158                 .withExpectedCount(1).build();
    159 
    160         try {
    161             // Assert that values match from cursor
    162             ContentProviderResult result = op1.apply(new TestContentProvider() {
    163                 public Cursor query(Uri uri, String[] projection, String selection,
    164                         String[] selectionArgs, String sortOrder) {
    165                     // Return cursor over specific set of values
    166                     return getCursor(sTestValues1, 1);
    167                 }
    168             }, null, 0);
    169         } catch (OperationApplicationException e) {
    170             fail("newAssert() failed");
    171         }
    172 
    173         ContentProviderOperation op2 = ContentProviderOperation.newAssertQuery(sTestUri1)
    174                 .withExpectedCount(0).build();
    175 
    176         try {
    177             // Assert that values match from cursor
    178             ContentProviderResult result = op2.apply(new TestContentProvider() {
    179                 public Cursor query(Uri uri, String[] projection, String selection,
    180                         String[] selectionArgs, String sortOrder) {
    181                     // Return cursor over specific set of values
    182                     return getCursor(sTestValues1, 0);
    183                 }
    184             }, null, 0);
    185         } catch (OperationApplicationException e) {
    186             fail("newAssert() failed");
    187         }
    188 
    189         ContentProviderOperation op3 = ContentProviderOperation.newAssertQuery(sTestUri1)
    190                 .withExpectedCount(2).build();
    191 
    192         try {
    193             // Assert that values match from cursor
    194             ContentProviderResult result = op3.apply(new TestContentProvider() {
    195                 public Cursor query(Uri uri, String[] projection, String selection,
    196                         String[] selectionArgs, String sortOrder) {
    197                     // Return cursor over specific set of values
    198                     return getCursor(sTestValues1, 5);
    199                 }
    200             }, null, 0);
    201             fail("we expect the exception to be thrown");
    202         } catch (OperationApplicationException e) {
    203         }
    204     }
    205 
    206     /**
    207      * Build a {@link Cursor} with a single row that contains all values
    208      * provided through the given {@link ContentValues}.
    209      */
    210     private Cursor getCursor(ContentValues contentValues, int numRows) {
    211         final Set<Entry<String, Object>> valueSet = contentValues.valueSet();
    212         final String[] keys = new String[valueSet.size()];
    213         final Object[] values = new Object[valueSet.size()];
    214 
    215         int i = 0;
    216         for (Entry<String, Object> entry : valueSet) {
    217             keys[i] = entry.getKey();
    218             values[i] = entry.getValue();
    219             i++;
    220         }
    221 
    222         final MatrixCursor cursor = new MatrixCursor(keys);
    223         for (i = 0; i < numRows; i++) {
    224             cursor.addRow(values);
    225         }
    226         return cursor;
    227     }
    228 
    229     public void testValueBackRefs() {
    230         ContentValues values = new ContentValues();
    231         values.put("a", "in1");
    232         values.put("a2", "in2");
    233         values.put("b", "in3");
    234         values.put("c", "in4");
    235 
    236         ContentProviderResult[] previousResults = new ContentProviderResult[4];
    237         previousResults[0] = new ContentProviderResult(100);
    238         previousResults[1] = new ContentProviderResult(101);
    239         previousResults[2] = new ContentProviderResult(102);
    240         previousResults[3] = new ContentProviderResult(103);
    241 
    242         ContentValues expectedValues = new ContentValues(values);
    243         expectedValues.put("a1", (long) 103);
    244         expectedValues.put("a2", (long) 101);
    245         expectedValues.put("a3", (long) 102);
    246 
    247         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
    248                 .withValues(values)
    249                 .withValueBackReference("a1", 3)
    250                 .withValueBackReference("a2", 1)
    251                 .withValueBackReference("a3", 2)
    252                 .build();
    253         ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length);
    254         assertEquals(expectedValues, v2);
    255     }
    256 
    257     public void testSelectionBackRefs() {
    258         ContentProviderResult[] previousResults = new ContentProviderResult[4];
    259         previousResults[0] = new ContentProviderResult(100);
    260         previousResults[1] = new ContentProviderResult(101);
    261         previousResults[2] = new ContentProviderResult(102);
    262         previousResults[3] = new ContentProviderResult(103);
    263 
    264         String[] selectionArgs = new String[]{"a", null, null, "b", null};
    265 
    266         final ContentValues values = new ContentValues();
    267         values.put("unused", "unused");
    268 
    269         ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1)
    270                 .withSelectionBackReference(1, 3)
    271                 .withSelectionBackReference(2, 1)
    272                 .withSelectionBackReference(4, 2)
    273                 .withSelection("unused", selectionArgs)
    274                 .withValues(values)
    275                 .build();
    276         String[] s2 = op1.resolveSelectionArgsBackReferences(
    277                 previousResults, previousResults.length);
    278         assertEquals("a,103,101,b,102", TextUtils.join(",", s2));
    279     }
    280 
    281     public void testParcelingOperation() throws NoSuchFieldException, IllegalAccessException,
    282             NoSuchMethodException, InvocationTargetException, InstantiationException {
    283         Parcel parcel = Parcel.obtain();
    284         ContentProviderOperation op1;
    285         ContentProviderOperation op2;
    286 
    287         HashMap<Integer, Integer> selArgsBackRef = new HashMap<Integer, Integer>();
    288         selArgsBackRef.put(1, 2);
    289         selArgsBackRef.put(3, 4);
    290 
    291         ContentValues values = new ContentValues();
    292         values.put("v1", "val1");
    293         values.put("v2", "43");
    294 
    295         ContentValues valuesBackRef = new ContentValues();
    296         values.put("v3", "val3");
    297         values.put("v4", "44");
    298 
    299         try {
    300             ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(
    301                     Uri.parse("content://goo/bar"));
    302 
    303             builderSetExpectedCount(builder, 42);
    304             builderSetSelection(builder, "selection");
    305             builderSetSelectionArgs(builder, new String[]{"a", "b"});
    306             builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
    307             builderSetValues(builder, values);
    308             builderSetValuesBackReferences(builder, valuesBackRef);
    309 
    310             op1 = newOperationFromBuilder(builder);
    311             op1.writeToParcel(parcel, 0);
    312             parcel.setDataPosition(0);
    313             op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
    314 
    315             assertEquals(ContentProviderOperation.TYPE_INSERT, operationGetType(op2));
    316             assertEquals("content://goo/bar", operationGetUri(op2).toString());
    317             assertEquals(Integer.valueOf(42), operationGetExpectedCount(op2));
    318             assertEquals("selection", operationGetSelection(op2));
    319             assertEquals(2, operationGetSelectionArgs(op2).length);
    320             assertEquals("a", operationGetSelectionArgs(op2)[0]);
    321             assertEquals("b", operationGetSelectionArgs(op2)[1]);
    322             assertEquals(values, operationGetValues(op2));
    323             assertEquals(valuesBackRef, operationGetValuesBackReferences(op2));
    324             assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
    325             assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
    326             assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
    327         } finally {
    328             parcel.recycle();
    329         }
    330 
    331         try {
    332             ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(
    333                     Uri.parse("content://goo/bar"));
    334 
    335             builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
    336 
    337             op1 = newOperationFromBuilder(builder);
    338             op1.writeToParcel(parcel, 0);
    339             parcel.setDataPosition(0);
    340             op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
    341             assertEquals(ContentProviderOperation.TYPE_UPDATE, operationGetType(op2));
    342             assertEquals("content://goo/bar", operationGetUri(op2).toString());
    343             assertNull(operationGetExpectedCount(op2));
    344             assertNull(operationGetSelection(op2));
    345             assertNull(operationGetSelectionArgs(op2));
    346             assertNull(operationGetValues(op2));
    347             assertNull(operationGetValuesBackReferences(op2));
    348             assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
    349             assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
    350             assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
    351         } finally {
    352             parcel.recycle();
    353         }
    354 
    355         try {
    356             ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete(
    357                     Uri.parse("content://goo/bar"));
    358 
    359             op1 = newOperationFromBuilder(builder);
    360             op1.writeToParcel(parcel, 0);
    361             parcel.setDataPosition(0);
    362             op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
    363             assertEquals(ContentProviderOperation.TYPE_DELETE, operationGetType(op2));
    364             assertEquals("content://goo/bar", operationGetUri(op2).toString());
    365             assertNull(operationGetExpectedCount(op2));
    366             assertNull(operationGetSelection(op2));
    367             assertNull(operationGetSelectionArgs(op2));
    368             assertNull(operationGetValues(op2));
    369             assertNull(operationGetValuesBackReferences(op2));
    370             assertNull(operationGetSelectionArgsBackReferences(op2));
    371         } finally {
    372             parcel.recycle();
    373         }
    374     }
    375 
    376     private static ContentProviderOperation newOperationFromBuilder(
    377             ContentProviderOperation.Builder builder)
    378             throws NoSuchMethodException, InstantiationException, IllegalAccessException,
    379             InvocationTargetException {
    380         final Constructor constructor = CLASS_OPERATION.getDeclaredConstructor(CLASS_BUILDER);
    381         constructor.setAccessible(true);
    382         return (ContentProviderOperation) constructor.newInstance(builder);
    383     }
    384 
    385     private void builderSetSelectionArgsBackReferences(
    386             ContentProviderOperation.Builder builder, HashMap<Integer, Integer> selArgsBackRef)
    387             throws NoSuchFieldException, IllegalAccessException {
    388         Field field;
    389         field = CLASS_BUILDER.getDeclaredField("mSelectionArgsBackReferences");
    390         field.setAccessible(true);
    391         field.set(builder, selArgsBackRef);
    392     }
    393 
    394     private void builderSetValuesBackReferences(
    395             ContentProviderOperation.Builder builder, ContentValues valuesBackReferences)
    396             throws NoSuchFieldException, IllegalAccessException {
    397         Field field;
    398         field = CLASS_BUILDER.getDeclaredField("mValuesBackReferences");
    399         field.setAccessible(true);
    400         field.set(builder, valuesBackReferences);
    401     }
    402 
    403     private void builderSetSelection(
    404             ContentProviderOperation.Builder builder, String selection)
    405             throws NoSuchFieldException, IllegalAccessException {
    406         Field field;
    407         field = CLASS_BUILDER.getDeclaredField("mSelection");
    408         field.setAccessible(true);
    409         field.set(builder, selection);
    410     }
    411 
    412     private void builderSetSelectionArgs(
    413             ContentProviderOperation.Builder builder, String[] selArgs)
    414             throws NoSuchFieldException, IllegalAccessException {
    415         Field field;
    416         field = CLASS_BUILDER.getDeclaredField("mSelectionArgs");
    417         field.setAccessible(true);
    418         field.set(builder, selArgs);
    419     }
    420 
    421     private void builderSetValues(
    422             ContentProviderOperation.Builder builder, ContentValues values)
    423             throws NoSuchFieldException, IllegalAccessException {
    424         Field field;
    425         field = CLASS_BUILDER.getDeclaredField("mValues");
    426         field.setAccessible(true);
    427         field.set(builder, values);
    428     }
    429 
    430     private void builderSetExpectedCount(
    431             ContentProviderOperation.Builder builder, Integer expectedCount)
    432             throws NoSuchFieldException, IllegalAccessException {
    433         Field field;
    434         field = CLASS_BUILDER.getDeclaredField("mExpectedCount");
    435         field.setAccessible(true);
    436         field.set(builder, expectedCount);
    437     }
    438 
    439     private int operationGetType(ContentProviderOperation operation)
    440             throws NoSuchFieldException, IllegalAccessException {
    441         final Field field = CLASS_OPERATION.getDeclaredField("mType");
    442         field.setAccessible(true);
    443         return field.getInt(operation);
    444     }
    445 
    446     private Uri operationGetUri(ContentProviderOperation operation)
    447             throws NoSuchFieldException, IllegalAccessException {
    448         final Field field = CLASS_OPERATION.getDeclaredField("mUri");
    449         field.setAccessible(true);
    450         return (Uri) field.get(operation);
    451     }
    452 
    453     private String operationGetSelection(ContentProviderOperation operation)
    454             throws NoSuchFieldException, IllegalAccessException {
    455         final Field field = CLASS_OPERATION.getDeclaredField("mSelection");
    456         field.setAccessible(true);
    457         return (String) field.get(operation);
    458     }
    459 
    460     private String[] operationGetSelectionArgs(ContentProviderOperation operation)
    461             throws NoSuchFieldException, IllegalAccessException {
    462         final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgs");
    463         field.setAccessible(true);
    464         return (String[]) field.get(operation);
    465     }
    466 
    467     private ContentValues operationGetValues(ContentProviderOperation operation)
    468             throws NoSuchFieldException, IllegalAccessException {
    469         final Field field = CLASS_OPERATION.getDeclaredField("mValues");
    470         field.setAccessible(true);
    471         return (ContentValues) field.get(operation);
    472     }
    473 
    474     private Integer operationGetExpectedCount(ContentProviderOperation operation)
    475             throws NoSuchFieldException, IllegalAccessException {
    476         final Field field = CLASS_OPERATION.getDeclaredField("mExpectedCount");
    477         field.setAccessible(true);
    478         return (Integer) field.get(operation);
    479     }
    480 
    481     private ContentValues operationGetValuesBackReferences(ContentProviderOperation operation)
    482             throws NoSuchFieldException, IllegalAccessException {
    483         final Field field = CLASS_OPERATION.getDeclaredField("mValuesBackReferences");
    484         field.setAccessible(true);
    485         return (ContentValues) field.get(operation);
    486     }
    487 
    488     private Map<Integer, Integer> operationGetSelectionArgsBackReferences(
    489             ContentProviderOperation operation)
    490             throws NoSuchFieldException, IllegalAccessException {
    491         final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgsBackReferences");
    492         field.setAccessible(true);
    493         return (Map<Integer, Integer>) field.get(operation);
    494     }
    495 
    496     public void testParcelingResult() {
    497         Parcel parcel = Parcel.obtain();
    498         ContentProviderResult result1;
    499         ContentProviderResult result2;
    500         try {
    501             result1 = new ContentProviderResult(Uri.parse("content://goo/bar"));
    502             result1.writeToParcel(parcel, 0);
    503             parcel.setDataPosition(0);
    504             result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
    505             assertEquals("content://goo/bar", result2.uri.toString());
    506             assertNull(result2.count);
    507         } finally {
    508             parcel.recycle();
    509         }
    510 
    511         parcel = Parcel.obtain();
    512         try {
    513             result1 = new ContentProviderResult(42);
    514             result1.writeToParcel(parcel, 0);
    515             parcel.setDataPosition(0);
    516             result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
    517             assertEquals(Integer.valueOf(42), result2.count);
    518             assertNull(result2.uri);
    519         } finally {
    520             parcel.recycle();
    521         }
    522     }
    523 
    524     static class TestContentProvider extends ContentProvider {
    525         public boolean onCreate() {
    526             throw new UnsupportedOperationException();
    527         }
    528 
    529         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
    530                 String sortOrder) {
    531             throw new UnsupportedOperationException();
    532         }
    533 
    534         public String getType(Uri uri) {
    535             throw new UnsupportedOperationException();
    536         }
    537 
    538         public Uri insert(Uri uri, ContentValues values) {
    539             throw new UnsupportedOperationException();
    540         }
    541 
    542         public int delete(Uri uri, String selection, String[] selectionArgs) {
    543             throw new UnsupportedOperationException();
    544         }
    545 
    546         public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    547             throw new UnsupportedOperationException();
    548         }
    549     }
    550 }