Android Open Source - android-memento Abstract Sqlite Content Provider Integration Test






From Project

Back to project page android-memento.

License

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.

Java Source Code

/*
 * 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();
        }
    }
}




Java Source Code List

com.twofortyfouram.memento.debug.provider.SqliteContentProviderImpl.java
com.twofortyfouram.memento.debug.provider.SqliteOpenHelperImpl.java
com.twofortyfouram.memento.debug.provider.SqliteUriMatcherImpl.java
com.twofortyfouram.memento.debug.provider.TableOneContract.java
com.twofortyfouram.memento.provider.ContentChangeNotificationQueueTest.java
com.twofortyfouram.memento.provider.ContentChangeNotificationQueue.java
com.twofortyfouram.memento.provider.ContentProviderOperationServiceTest.java
com.twofortyfouram.memento.provider.ContentProviderOperationService.java
com.twofortyfouram.memento.provider.ContentProviderUtilTest.java
com.twofortyfouram.memento.provider.ContentProviderUtil.java
com.twofortyfouram.memento.provider.ImmutableUriMatcherTest.java
com.twofortyfouram.memento.provider.ImmutableUriMatcher.java
com.twofortyfouram.memento.provider.sqlite.AbstractSqliteContentProviderIntegrationTest.java
com.twofortyfouram.memento.provider.sqlite.AbstractSqliteContentProviderTest.java
com.twofortyfouram.memento.provider.sqlite.AbstractSqliteContentProvider.java
com.twofortyfouram.memento.provider.sqlite.SqliteColumnBuilderTest.java
com.twofortyfouram.memento.provider.sqlite.SqliteColumnBuilder.java
com.twofortyfouram.memento.provider.sqlite.SqliteIndexBuilderTest.java
com.twofortyfouram.memento.provider.sqlite.SqliteIndexBuilder.java
com.twofortyfouram.memento.provider.sqlite.SqliteOpenHelperCompat.java
com.twofortyfouram.memento.provider.sqlite.SqliteStorageClass.java
com.twofortyfouram.memento.provider.sqlite.SqliteTableBuilderTest.java
com.twofortyfouram.memento.provider.sqlite.SqliteTableBuilder.java
com.twofortyfouram.memento.provider.sqlite.SqliteUriMatchTest.java
com.twofortyfouram.memento.provider.sqlite.SqliteUriMatch.java
com.twofortyfouram.memento.provider.sqlite.SqliteUriMatcher.java