Back to project page android-memento.
The source code is released under:
Apache License
If you think the Android project android-memento listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * android-memento-lib https://github.com/twofortyfouram/android-memento * Copyright 2014 two forty four a.m. LLC */* w w w .jav a 2s . co m*/ * 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 com.twofortyfouram.memento.provider.sqlite; import com.twofortyfouram.annotation.Slow; import com.twofortyfouram.annotation.Slow.Speed; import com.twofortyfouram.memento.debug.provider.SqliteContentProviderImpl; import com.twofortyfouram.memento.debug.provider.TableOneContract; import com.twofortyfouram.spackle.util.ThreadUtil; import com.twofortyfouram.spackle.util.ThreadUtil.ThreadPriority; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.ContentObserver; import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; import android.support.annotation.NonNull; import android.test.ProviderTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.format.DateUtils; import java.util.ArrayList; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Tests the {@link SqliteContentProviderImpl}, a minimal implementation of the abstract class, to * test {@link AbstractSqliteContentProvider}. */ public final class AbstractSqliteContentProviderIntegrationTest extends ProviderTestCase2<SqliteContentProviderImpl> { public AbstractSqliteContentProviderIntegrationTest() { super(SqliteContentProviderImpl.class, "com.twofortyfouram.memento.debug.provider"); //$NON-NLS-1$ } @SmallTest public void testUpdate_content_notification_success() { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); // Insert a record and wait for the content notification to clear TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 1); try { final ContentValues initialValues = TableOneContract .getContentValues("test_value"); //$NON-NLS-1$ resolver.insert(TableOneContract.getContentUri(getContext()), initialValues); observer.assertExpectedHits(); } finally { observer.destroy(); observer = null; } final ContentValues updatedValues = TableOneContract .getContentValues("test_value_updated"); //$NON-NLS-1$ observer = getNewRegisteredContentObserver(TableOneContract.getContentUri(getContext()), 1); try { assertEquals(1, resolver.update(TableOneContract.getContentUri(getContext()), updatedValues, null, null)); observer.assertExpectedHits(); } finally { observer.destroy(); observer = null; } } @MediumTest public void testUpdate_content_notification_no_success() { final ContentResolver resolver = getContext().getContentResolver(); final ContentValues initialValues = TableOneContract .getContentValues("test_value"); //$NON-NLS-1$ // Insert a record and wait for the content notification to clear TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 1); try { resolver.insert(TableOneContract.getContentUri(getContext()), initialValues); observer.assertExpectedHits(); } finally { observer.destroy(); observer = null; } final ContentValues updatedValues = TableOneContract .getContentValues("test_value_updated"); //$NON-NLS-1$ observer = getNewRegisteredContentObserver(TableOneContract.getContentUri(getContext()), 0); try { final String selection = String.format(Locale.US, "%s = ?", TableOneContract.COLUMN_STRING_COLUMN_ONE); //$NON-NLS-1$ final String[] selectionArgs = { "bork_bork_bork"}; //$NON-NLS-1$ assertEquals(0, resolver.update(TableOneContract.getContentUri(getContext()), updatedValues, selection, selectionArgs)); observer.assertExpectedHits(); } finally { observer.destroy(); observer = null; } } @SmallTest public void testDelete_content_notification_success() { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); final ContentValues[] initialValues = { TableOneContract.getContentValues("test_value_one"), TableOneContract.getContentValues("test_value_two"), TableOneContract .getContentValues("test_value_three")}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ assertEquals(3, resolver.bulkInsert(TableOneContract.getContentUri(getContext()), initialValues)); SystemClock.sleep(500); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 1); try { final String selection = String.format(Locale.US, "%s = ?", TableOneContract.COLUMN_STRING_COLUMN_ONE); //$NON-NLS-1$ final String[] selectionArgs = { "test_value_two"}; //$NON-NLS-1$ assertEquals(1, resolver.delete(TableOneContract.getContentUri(getContext()), selection, selectionArgs)); assertCount(resolver, 2); observer.assertExpectedHits(); } finally { observer.destroy(); } } @MediumTest public void testDelete_content_notification_no_success() { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); resolver.insert(TableOneContract.getContentUri(getContext()), TableOneContract.getContentValues("test_value_one")); //$NON-NLS-1$ resolver.insert(TableOneContract.getContentUri(getContext()), TableOneContract.getContentValues("test_value_two")); //$NON-NLS-1$ resolver.insert(TableOneContract.getContentUri(getContext()), TableOneContract.getContentValues("test_value_three")); //$NON-NLS-1$ SystemClock.sleep(500); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 0); try { final String selection = String.format(Locale.US, "%s = ?", TableOneContract.COLUMN_STRING_COLUMN_ONE); //$NON-NLS-1$ final String[] selectionArgs = { "test_value_four"}; //$NON-NLS-1$ assertEquals(0, resolver.delete(TableOneContract.getContentUri(getContext()), selection, selectionArgs)); assertCount(resolver, 3); observer.assertExpectedHits(); } finally { observer.destroy(); } } @MediumTest public void testApplyBatch_content_notification_abort() throws RemoteException { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); SystemClock.sleep(500); /* * When an operation is aborted, none of the operations should be performed. * * In addition, no content notification should occur. */ final ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); ops.add(ContentProviderOperation.newInsert(TableOneContract.getContentUri(getContext())) .withValues(TableOneContract.getContentValues("table_name")).build()); //$NON-NLS-1$ // This query will fail, aborting the entire transaction ops.add(ContentProviderOperation .newAssertQuery(TableOneContract.getContentUri(getContext())).withExpectedCount(5) .build()); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 0); try { // First, the failed operation should throw an exception. try { resolver.applyBatch(SqliteContentProviderImpl.getContentAuthority(getContext()), ops); fail("Should have thrown an exception"); //$NON-NLS-1$ } catch (final OperationApplicationException e) { // Expected exception } assertCount(resolver, 0); // Finally, the failed operation should not post a content change // notification. observer.assertExpectedHits(); } finally { observer.destroy(); } } @MediumTest public void testApplyBatch_content_notification_success() throws RemoteException, OperationApplicationException { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); SystemClock.sleep(1 * DateUtils.SECOND_IN_MILLIS); final ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); ops.add(ContentProviderOperation.newInsert(TableOneContract.getContentUri(getContext())) .withValues(TableOneContract.getContentValues("table_name")).build()); //$NON-NLS-1$ ops.add(ContentProviderOperation.newInsert(TableOneContract.getContentUri(getContext())) .withValues(TableOneContract.getContentValues("table_name")).build()); //$NON-NLS-1$ ops.add(ContentProviderOperation.newInsert(TableOneContract.getContentUri(getContext())) .withValues(TableOneContract.getContentValues("table_name")).build()); //$NON-NLS-1$ ops.add(ContentProviderOperation.newUpdate(TableOneContract.getContentUri(getContext())) .withValues(TableOneContract.getContentValues("table_name_updated")) .build()); //$NON-NLS-1$ final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 1); try { getContext().getContentResolver().applyBatch( SqliteContentProviderImpl.getContentAuthority(getContext()), ops); assertCount(resolver, 3); observer.assertExpectedHits(); } finally { observer.destroy(); } } @SmallTest public void testBulkInsert_content_notification_success() { final ContentResolver resolver = getContext().getContentResolver(); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 1); try { final ContentValues[] contentValues = { TableOneContract.getContentValues("test_value_one"), //$NON-NLS-1$ TableOneContract.getContentValues("test_value_two")}; //$NON-NLS-1$ assertEquals(2, resolver.bulkInsert(TableOneContract.getContentUri(getContext()), contentValues)); assertCount(resolver, 2); observer.assertExpectedHits(); } finally { observer.destroy(); } } @MediumTest public void testBulkInsert_content_notification_abort() { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); SystemClock.sleep(500); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 0); try { try { /* * Null violates constraints, so this will throw an exception. */ final ContentValues values = new ContentValues(1); values.putNull(TableOneContract.COLUMN_STRING_COLUMN_ONE); final ContentValues[] contentValues = { TableOneContract.getContentValues("test_value_one"), //$NON-NLS-1$ values }; resolver.bulkInsert(TableOneContract.getContentUri(getContext()), contentValues); fail(); } catch (final SQLiteException e) { // Expected exception } assertCount(resolver, 0); observer.assertExpectedHits(); } finally { observer.destroy(); } } @SmallTest public void testRunInTransaction_nested_multiple_threads() { /* * This tests that transactions are mutually exclusive. The second transaction should be * blocked from execution until the first transaction completes. */ final ContentResolver resolver = getContext().getContentResolver(); final CountDownLatch transactionOneStartLatch = new CountDownLatch(1); final CountDownLatch keepTransactionAliveLatch = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { AbstractSqliteContentProvider.runInTransaction(getContext(), SqliteContentProviderImpl.getContentAuthority(getContext()), new Runnable() { @Override public void run() { transactionOneStartLatch.countDown(); resolver.insert(TableOneContract.getContentUri(getContext()), TableOneContract .getContentValues("test_value")); //$NON-NLS-1$ try { assertFalse(keepTransactionAliveLatch.await( 1 * DateUtils.SECOND_IN_MILLIS, TimeUnit.MILLISECONDS)); } catch (final InterruptedException e) { throw new AssertionError(e); } } }); } }).start(); final CountDownLatch threadTwoLatch = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { try { transactionOneStartLatch.await(); } catch (final InterruptedException e) { throw new AssertionError(e); } AbstractSqliteContentProvider.runInTransaction(getContext(), SqliteContentProviderImpl.getContentAuthority(getContext()), new Runnable() { @Override public void run() { resolver.delete(TableOneContract.getContentUri(getContext()), null, null); keepTransactionAliveLatch.countDown(); } }); threadTwoLatch.countDown(); } }).start(); try { threadTwoLatch.await(); } catch (final InterruptedException e) { throw new AssertionError(e); } assertCount(resolver, 0); } @LargeTest public void testRunInTransaction_stress() { for (int x = 0; x < 30; x++) { testRunInTransaction_nested_multiple_threads(); } } @SmallTest public void testRunInTransaction_content_notification_success() { final ContentResolver resolver = getContext().getContentResolver(); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 1); try { AbstractSqliteContentProvider.runInTransaction(getContext(), SqliteContentProviderImpl.getContentAuthority(getContext()), new Runnable() { @Override public void run() { resolver.insert(TableOneContract.getContentUri(getContext()), TableOneContract.getContentValues("test_value_one")); //$NON-NLS-1$ } }); assertCount(resolver, 1); observer.assertExpectedHits(); } finally { observer.destroy(); } } @MediumTest public void testRunInTransaction_content_notification_abort() { final ContentResolver resolver = getContext().getContentResolver(); resolver.delete(TableOneContract.getContentUri(getContext()), null, null); SystemClock.sleep(500); final TestContentObserver observer = getNewRegisteredContentObserver( TableOneContract.getContentUri(getContext()), 0); try { try { AbstractSqliteContentProvider.runInTransaction(getContext(), SqliteContentProviderImpl.getContentAuthority(getContext()), new Runnable() { @Override public void run() { resolver.insert(TableOneContract.getContentUri(getContext()), TableOneContract .getContentValues("test_value_one")); //$NON-NLS-1$ // This exception should abort the // transaction throw new RuntimeException(); } }); fail(); } catch (final RuntimeException e) { // Expected exception } assertCount(resolver, 0); observer.assertExpectedHits(); } finally { observer.destroy(); } } /** * Asserts that {@link TableOneContract} has {@code count} rows. * * @param resolver ContentResolver to use. * @param count Number of rows to assert exist in the table. */ private void assertCount(@NonNull final ContentResolver resolver, final int count) { Cursor cursor = null; try { cursor = resolver.query(TableOneContract.getContentUri(getContext()), null, null, null, null); assertEquals(count, cursor.getCount()); } finally { if (null != cursor) { cursor.close(); cursor = null; } } } /** * Helper to register a ContentObserver to listen for content change notifications. * * @param uriToMonitor Uri to monitor for hits. * @param expectedHitCount Expected number of hits. * @return A registered content observer monitoring for hits. */ @NonNull public TestContentObserver getNewRegisteredContentObserver(@NonNull final Uri uriToMonitor, final int expectedHitCount) { final Handler handler = new Handler(ThreadUtil.newHandlerThread( TestContentObserver.class.getName(), ThreadPriority.DEFAULT).getLooper()); /* * The MockContentResolver won't deliver content change notifications, so a real * ContentResolver is required for this test. */ final ContentResolver resolver = getContext().getContentResolver(); final TestContentObserver observer = new TestContentObserver(handler, resolver, expectedHitCount); resolver.registerContentObserver(uriToMonitor, true, observer); return observer; } private static final class TestContentObserver extends ContentObserver { /** * Amount of time to wait for an asynchronous event to complete. */ /* * This value is somewhat arbitrary. A number of tests need to wait for asynchronous events. * The longer the wait time, the more likely that the test has completed in the background. * On the flip side, setting this value higher also makes the test take significantly longer * to complete. */ private static final int DEFAULT_LATCH_WAIT_MILLIS = 1500; /** * Background handler for receiving content change notifications. */ @NonNull private final Handler mHandler; /** * ContentResolver on which this observer is registered. */ @NonNull private final ContentResolver mResolver; /** * Latch to wait for hits. */ @NonNull private final CountDownLatch mLatch; /** * Number of hits received. */ @NonNull private final AtomicInteger mHitCount = new AtomicInteger(0); /** * Expected number of hits. */ private final int mExpectedHitCount; public TestContentObserver(@NonNull final Handler handler, @NonNull final ContentResolver resolver, final int expectedHitCount) { super(handler); mHandler = handler; mResolver = resolver; mExpectedHitCount = expectedHitCount; mLatch = new CountDownLatch(expectedHitCount); } @Override public void onChange(final boolean selfChange) { super.onChange(selfChange); mHitCount.incrementAndGet(); mLatch.countDown(); } /** * Asserts the expected number of hits were received. */ @Slow(Speed.SECONDS) public void assertExpectedHits() { if (0 == mExpectedHitCount) { SystemClock.sleep(DEFAULT_LATCH_WAIT_MILLIS); assertEquals(mExpectedHitCount, mHitCount.get()); } else { try { assertTrue(mLatch.await(DEFAULT_LATCH_WAIT_MILLIS, TimeUnit.MILLISECONDS)); } catch (final InterruptedException e) { throw new RuntimeException(e); } } } /** * Unregisters this class. */ public void destroy() { mResolver.unregisterContentObserver(this); mHandler.getLooper().quit(); } } }