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 17package android.content; 18 19import android.content.ContentValues; 20import android.database.Cursor; 21import android.database.MatrixCursor; 22import android.net.Uri; 23import android.os.Parcel; 24import android.test.suitebuilder.annotation.SmallTest; 25import android.text.TextUtils; 26import junit.framework.TestCase; 27 28import java.lang.reflect.Constructor; 29import java.lang.reflect.Field; 30import java.lang.reflect.InvocationTargetException; 31import java.util.HashMap; 32import java.util.Set; 33import java.util.Map; 34import java.util.Map.Entry; 35 36@SmallTest 37public 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}