Home | History | Annotate | Download | only in notepad
      1 /*
      2  * Copyright (C) 2010 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 com.example.android.notepad;
     18 
     19 import android.content.ContentUris;
     20 import android.content.ContentValues;
     21 import android.content.res.AssetFileDescriptor;
     22 import android.database.Cursor;
     23 import android.database.sqlite.SQLiteDatabase;
     24 import android.net.Uri;
     25 import android.os.ParcelFileDescriptor;
     26 import android.test.ProviderTestCase2;
     27 import android.test.mock.MockContentResolver;
     28 
     29 import java.io.BufferedReader;
     30 import java.io.FileDescriptor;
     31 import java.io.FileNotFoundException;
     32 import java.io.FileReader;
     33 import java.io.IOException;
     34 import java.util.Calendar;
     35 import java.util.GregorianCalendar;
     36 
     37 /*
     38  */
     39 /**
     40  * This class tests the content provider for the Note Pad sample application.
     41  *
     42  * To learn how to run an entire test package or one of its classes, please see
     43  * "Testing in Eclipse, with ADT" or "Testing in Other IDEs" in the Developer Guide.
     44  */
     45 public class NotePadProviderTest extends ProviderTestCase2<NotePadProvider> {
     46 
     47     // A URI that the provider does not offer, for testing error handling.
     48     private static final Uri INVALID_URI =
     49         Uri.withAppendedPath(NotePad.Notes.CONTENT_URI, "invalid");
     50 
     51     // Contains a reference to the mocked content resolver for the provider under test.
     52     private MockContentResolver mMockResolver;
     53 
     54     // Contains an SQLite database, used as test data
     55     private SQLiteDatabase mDb;
     56 
     57     // Contains the test data, as an array of NoteInfo instances.
     58     private final NoteInfo[] TEST_NOTES = {
     59         new NoteInfo("Note0", "This is note 0"),
     60         new NoteInfo("Note1", "This is note 1"),
     61         new NoteInfo("Note2", "This is note 2"),
     62         new NoteInfo("Note3", "This is note 3"),
     63         new NoteInfo("Note4", "This is note 4"),
     64         new NoteInfo("Note5", "This is note 5"),
     65         new NoteInfo("Note6", "This is note 6"),
     66         new NoteInfo("Note7", "This is note 7"),
     67         new NoteInfo("Note8", "This is note 8"),
     68         new NoteInfo("Note9", "This is note 9") };
     69 
     70     // Number of milliseconds in one day (milliseconds * seconds * minutes * hours)
     71     private static final long ONE_DAY_MILLIS = 1000 * 60 * 60 * 24;
     72 
     73     // Number of milliseconds in one week
     74     private static final long ONE_WEEK_MILLIS = ONE_DAY_MILLIS * 7;
     75 
     76     // Creates a calendar object equal to January 1, 2010 at 12 midnight
     77     private static final GregorianCalendar TEST_CALENDAR =
     78         new GregorianCalendar(2010, Calendar.JANUARY, 1, 0, 0, 0);
     79 
     80     // Stores a timestamp value, set to an arbitrary starting point
     81     private final static long START_DATE = TEST_CALENDAR.getTimeInMillis();
     82 
     83     // Sets a MIME type filter, used to test provider methods that return more than one MIME type
     84     // for a particular note. The filter will retrieve any MIME types supported for the content URI.
     85     private final static String MIME_TYPES_ALL = "*/*";
     86 
     87     // Sets a MIME type filter, used to test provider methods that return more than one MIME type
     88     // for a particular note. The filter is nonsense, so it will not retrieve any MIME types.
     89     private final static String MIME_TYPES_NONE = "qwer/qwer";
     90 
     91     // Sets a MIME type filter for plain text, used to the provider's methods that only handle
     92     // plain text
     93     private final static String MIME_TYPE_TEXT = "text/plain";
     94 
     95     /*
     96      * Constructor for the test case class.
     97      * Calls the super constructor with the class name of the provider under test and the
     98      * authority name of the provider.
     99      */
    100     public NotePadProviderTest() {
    101         super(NotePadProvider.class, NotePad.AUTHORITY);
    102     }
    103 
    104     /*
    105      * Sets up the test environment before each test method. Creates a mock content resolver,
    106      * gets the provider under test, and creates a new database for the provider.
    107      */
    108     @Override
    109     protected void setUp() throws Exception {
    110         // Calls the base class implementation of this method.
    111         super.setUp();
    112 
    113         // Gets the resolver for this test.
    114         mMockResolver = getMockContentResolver();
    115 
    116         /*
    117          * Gets a handle to the database underlying the provider. Gets the provider instance
    118          * created in super.setUp(), gets the DatabaseOpenHelper for the provider, and gets
    119          * a database object from the helper.
    120          */
    121         mDb = getProvider().getOpenHelperForTest().getWritableDatabase();
    122     }
    123 
    124     /*
    125      *  This method is called after each test method, to clean up the current fixture. Since
    126      *  this sample test case runs in an isolated context, no cleanup is necessary.
    127      */
    128     @Override
    129     protected void tearDown() throws Exception {
    130         super.tearDown();
    131     }
    132 
    133     /*
    134      * Sets up test data.
    135      * The test data is in an SQL database. It is created in setUp() without any data,
    136      * and populated in insertData if necessary.
    137      */
    138     private void insertData() {
    139         // Creates an instance of the ContentValues map type expected by database insertions
    140         ContentValues values = new ContentValues();
    141 
    142         // Sets up test data
    143         for (int index = 0; index < TEST_NOTES.length; index++) {
    144 
    145             // Set the creation and modification date for the note
    146             TEST_NOTES[index].setCreationDate(START_DATE + (index * ONE_DAY_MILLIS));
    147             TEST_NOTES[index].setModificationDate(START_DATE + (index * ONE_WEEK_MILLIS));
    148 
    149             // Adds a record to the database.
    150             mDb.insertOrThrow(
    151                 NotePad.Notes.TABLE_NAME,             // the table name for the insert
    152                 NotePad.Notes.COLUMN_NAME_TITLE,      // column set to null if empty values map
    153                 TEST_NOTES[index].getContentValues()  // the values map to insert
    154             );
    155         }
    156     }
    157 
    158     /*
    159      * Tests the provider's publicly available URIs. If the URI is not one that the provider
    160      * understands, the provider should throw an exception. It also tests the provider's getType()
    161      * method for each URI, which should return the MIME type associated with the URI.
    162      */
    163     public void testUriAndGetType() {
    164         // Tests the MIME type for the notes table URI.
    165         String mimeType = mMockResolver.getType(NotePad.Notes.CONTENT_URI);
    166         assertEquals(NotePad.Notes.CONTENT_TYPE, mimeType);
    167 
    168         // Tests the MIME type for the live folder URI.
    169         mimeType = mMockResolver.getType(NotePad.Notes.LIVE_FOLDER_URI);
    170         assertEquals(NotePad.Notes.CONTENT_TYPE, mimeType);
    171 
    172         // Creates a URI with a pattern for note ids. The id doesn't have to exist.
    173         Uri noteIdUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_ID_URI_BASE, 1);
    174 
    175         // Gets the note ID URI MIME type.
    176         mimeType = mMockResolver.getType(noteIdUri);
    177         assertEquals(NotePad.Notes.CONTENT_ITEM_TYPE, mimeType);
    178 
    179         // Tests an invalid URI. This should throw an IllegalArgumentException.
    180         mimeType = mMockResolver.getType(INVALID_URI);
    181     }
    182 
    183     /*
    184      * Tests the provider's stream MIME types returned by getStreamTypes(). If the provider supports
    185      * stream data for the URI, the MIME type is returned. Otherwise, the provider returns null.
    186      */
    187     public void testGetStreamTypes() {
    188 
    189         // Tests the notes table URI. This should return null, since the content provider does
    190         // not provide a stream MIME type for multiple notes.
    191         assertNull(mMockResolver.getStreamTypes(NotePad.Notes.CONTENT_URI, MIME_TYPES_ALL));
    192 
    193         // Tests the live folders URI. This should return null, since the content provider does not
    194         // provide a stream MIME type for multiple notes.
    195         assertNull(mMockResolver.getStreamTypes(NotePad.Notes.LIVE_FOLDER_URI, MIME_TYPES_ALL));
    196 
    197         /*
    198          * Tests the note id URI for a single note, using _ID value "1" which is a valid ID. Uses a
    199          * valid MIME type filter that will return all the supported MIME types for a content URI.
    200          * The result should be "text/plain".
    201          */
    202 
    203         // Constructs the note id URI
    204         Uri testUri = Uri.withAppendedPath(NotePad.Notes.CONTENT_ID_URI_BASE, "1");
    205 
    206         // Gets the MIME types for the URI, with the filter that selects all MIME types.
    207         String mimeType[] = mMockResolver.getStreamTypes(testUri, MIME_TYPES_ALL);
    208 
    209         // Tests that the result is not null and is equal to the expected value. Also tests that
    210         // only one MIME type is returned.
    211         assertNotNull(mimeType);
    212         assertEquals(mimeType[0],"text/plain");
    213         assertEquals(mimeType.length,1);
    214 
    215         /*
    216          * Tests with the same URI but with a filter that should not return any URIs.
    217          */
    218         mimeType = mMockResolver.getStreamTypes(testUri, MIME_TYPES_NONE);
    219         assertNull(mimeType);
    220 
    221         /*
    222          * Tests with a URI that should not have any associated stream MIME types, but with a
    223          * filter that returns all types. The result should still be null.
    224          */
    225         mimeType = mMockResolver.getStreamTypes(NotePad.Notes.CONTENT_URI, MIME_TYPES_ALL);
    226         assertNull(mimeType);
    227 
    228     }
    229 
    230     /*
    231      * Tests the provider's public API for opening a read-only pipe of data for a note ID URI
    232      * and MIME type filter matching "text/plain".
    233      * This method throws a FileNotFoundException if the URI isn't for a note ID or the MIME type
    234      * filter isn't "text/plain". It throws an IOException if it can't close a file descriptor.
    235      */
    236     public void testOpenTypedAssetFile() throws FileNotFoundException, IOException {
    237 
    238         // A URI to contain a note ID content URI.
    239         Uri testNoteIdUri;
    240 
    241         // A handle for the file descriptor returned by openTypedAssetFile().
    242         AssetFileDescriptor testAssetDescriptor;
    243 
    244         // Inserts data into the provider, so that the note ID URI will be recognized.
    245         insertData();
    246 
    247         // Constructs a URI with a note ID of 1. This matches the note ID URI pattern that
    248         // openTypedAssetFile can handle.
    249         testNoteIdUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_ID_URI_BASE, 1);
    250 
    251         // Opens the pipe. The opts argument is for passing options from a caller to the provider,
    252         // but the NotePadProvider does not use it.
    253         testAssetDescriptor = mMockResolver.openTypedAssetFileDescriptor(
    254                 testNoteIdUri,         // the URI for a single note. The pipe points to this
    255                                        // note's data
    256                 MIME_TYPE_TEXT,        // a MIME type of "text/plain"
    257                 null                   // the "opts" argument
    258         );
    259 
    260         // Gets the parcel file handle from the asset file handle.
    261         ParcelFileDescriptor testParcelDescriptor = testAssetDescriptor.getParcelFileDescriptor();
    262 
    263         // Gets the file handle from the asset file handle.
    264         FileDescriptor testDescriptor = testAssetDescriptor.getFileDescriptor();
    265 
    266         // Tests that the asset file handle is not null.
    267         assertNotNull(testAssetDescriptor);
    268 
    269         // Tests that the parcel file handle is not null.
    270         assertNotNull(testParcelDescriptor);
    271 
    272         // Tests that the file handle is not null.
    273         assertNotNull(testDescriptor);
    274 
    275         // Tests that the file handle is valid.
    276         assertTrue(testDescriptor.valid());
    277 
    278         // Closes the file handles.
    279         testParcelDescriptor.close();
    280         testAssetDescriptor.close();
    281 
    282         /*
    283          * Changes the URI to a notes URI for multiple notes, and re-test. This should fail, since
    284          * the provider does not support this type of URI. A FileNotFound exception is expected,
    285          * so call fail() if it does *not* occur.
    286          */
    287         try {
    288             testAssetDescriptor = mMockResolver.openTypedAssetFileDescriptor(
    289                     NotePad.Notes.CONTENT_URI,
    290                     MIME_TYPE_TEXT,
    291                     null
    292             );
    293             fail();
    294         } catch (FileNotFoundException e) {
    295             // continue
    296         }
    297 
    298         /*
    299          * Changes back to the note ID URI, but changes the MIME type filter to one that is not
    300          * supported by the provider. This should also fail, since the provider will only open a
    301          * pipe for MIME type "text/plain". A FileNotFound exception is expected, so calls
    302          * fail() if it does *not* occur.
    303          */
    304 
    305         try {
    306             testAssetDescriptor = mMockResolver.openTypedAssetFileDescriptor(
    307                     testNoteIdUri,
    308                     MIME_TYPES_NONE,
    309                     null
    310             );
    311             fail();
    312         } catch (FileNotFoundException e) {
    313             // continue
    314         }
    315 
    316     }
    317 
    318     /*
    319      * Tests the provider's method for actually returning writing data into a pipe. The method is
    320      * writeDataToPipe, but this method is not called directly. Instead, a caller invokes
    321      * openTypedAssetFile(). That method uses ContentProvider.openPipeHelper(), which has as one of
    322      * its arguments a ContentProvider.PipeDataWriter object that must actually put the data into
    323      * the pipe. PipeDataWriter is an interface, not a class, so it must be implemented.
    324      *
    325      * The NotePadProvider class itself implements the "ContentProvider.PipeDataWriter, which means
    326      * that it supplies the interface's only method, writeDataToPipe(). In effect, a call to
    327      * openTypedAssetFile() calls writeDataToPipe().
    328      *
    329      *  The test of writeDataToPipe() is separate from other tests of openTypedAssetFile() for the
    330      *  sake of clarity.
    331      */
    332     public void testWriteDataToPipe() throws FileNotFoundException {
    333 
    334         // A string array to hold the incoming data
    335         String[] inputData = {"","",""};
    336 
    337         // A URI for a note ID.
    338         Uri noteIdUri;
    339 
    340         // A Cursor to contain the retrieved note.
    341         Cursor noteIdCursor;
    342 
    343         // An AssetFileDescriptor for the pipe.
    344         AssetFileDescriptor noteIdAssetDescriptor;
    345 
    346         // The ParcelFileDescriptor in the AssetFileDescriptor
    347         ParcelFileDescriptor noteIdParcelDescriptor;
    348 
    349         // Inserts test data into the provider.
    350         insertData();
    351 
    352         // Creates note ID URI for a note that should now be in the provider.
    353         noteIdUri = ContentUris.withAppendedId(
    354                 NotePad.Notes.CONTENT_ID_URI_BASE,  // The base pattern for a note ID URI
    355                 1                                   // Sets the URI to point to record ID 1 in the
    356                                                     // provider
    357         );
    358 
    359         // Gets a Cursor for the note.
    360         noteIdCursor = mMockResolver.query(
    361                 noteIdUri,  // the URI for the note ID we want to retrieve
    362                 null,       // no projection, retrieve all the columns
    363                 null,       // no WHERE clause
    364                 null,       // no WHERE arguments
    365                 null        // default sort order
    366         );
    367 
    368         // Checks that the call worked.
    369         // a) Checks that the cursor is not null
    370         // b) Checks that it contains a single record
    371         assertNotNull(noteIdCursor);
    372         assertEquals(1,noteIdCursor.getCount());
    373 
    374         // Opens the pipe that will contain the data.
    375         noteIdAssetDescriptor = mMockResolver.openTypedAssetFileDescriptor(
    376                 noteIdUri,        // the URI of the note that will provide the data
    377                 MIME_TYPE_TEXT,   // the "text/plain" MIME type
    378                 null              // no other options
    379         );
    380 
    381         // Checks that the call worked.
    382         // a) checks that the AssetFileDescriptor is not null
    383         // b) gets its ParcelFileDescriptor
    384         // c) checks that the ParcelFileDescriptor is not null
    385         assertNotNull(noteIdAssetDescriptor);
    386         noteIdParcelDescriptor = noteIdAssetDescriptor.getParcelFileDescriptor();
    387         assertNotNull(noteIdParcelDescriptor);
    388 
    389         // Gets a File Reader that can read the pipe.
    390         FileReader fIn = new FileReader(noteIdParcelDescriptor.getFileDescriptor());
    391 
    392         // Gets a buffered reader wrapper for the File Reader. This allows reading line by line.
    393         BufferedReader bIn = new BufferedReader(fIn);
    394 
    395         /*
    396          * The pipe should contain three lines: The note's title, an empty line, and the note's
    397          * contents. The following code reads and stores these three lines.
    398          */
    399         for (int index = 0; index < inputData.length; index++) {
    400             try {
    401                 inputData[index] = bIn.readLine();
    402             } catch (IOException e) {
    403 
    404                 e.printStackTrace();
    405                 fail();
    406             }
    407         }
    408 
    409         // Asserts that the first record in the provider (written from TEST_NOTES[0]) has the same
    410         // note title as the first line retrieved from the pipe.
    411         assertEquals(TEST_NOTES[0].title, inputData[0]);
    412 
    413         // Asserts that the first record in the provider (written from TEST_NOTES[0]) has the same
    414         // note contents as the third line retrieved from the pipe.
    415         assertEquals(TEST_NOTES[0].note, inputData[2]);
    416     }
    417 
    418     /*
    419      * Tests the provider's public API for querying data in the table, using the URI for
    420      * a dataset of records.
    421      */
    422     public void testQueriesOnNotesUri() {
    423         // Defines a projection of column names to return for a query
    424         final String[] TEST_PROJECTION = {
    425             NotePad.Notes.COLUMN_NAME_TITLE,
    426             NotePad.Notes.COLUMN_NAME_NOTE,
    427             NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE
    428         };
    429 
    430         // Defines a selection column for the query. When the selection columns are passed
    431         // to the query, the selection arguments replace the placeholders.
    432         final String TITLE_SELECTION = NotePad.Notes.COLUMN_NAME_TITLE + " = " + "?";
    433 
    434         // Defines the selection columns for a query.
    435         final String SELECTION_COLUMNS =
    436             TITLE_SELECTION + " OR " + TITLE_SELECTION + " OR " + TITLE_SELECTION;
    437 
    438          // Defines the arguments for the selection columns.
    439         final String[] SELECTION_ARGS = { "Note0", "Note1", "Note5" };
    440 
    441          // Defines a query sort order
    442         final String SORT_ORDER = NotePad.Notes.COLUMN_NAME_TITLE + " ASC";
    443 
    444         // Query subtest 1.
    445         // If there are no records in the table, the returned cursor from a query should be empty.
    446         Cursor cursor = mMockResolver.query(
    447             NotePad.Notes.CONTENT_URI,  // the URI for the main data table
    448             null,                       // no projection, get all columns
    449             null,                       // no selection criteria, get all records
    450             null,                       // no selection arguments
    451             null                        // use default sort order
    452         );
    453 
    454          // Asserts that the returned cursor contains no records
    455         assertEquals(0, cursor.getCount());
    456 
    457          // Query subtest 2.
    458          // If the table contains records, the returned cursor from a query should contain records.
    459 
    460         // Inserts the test data into the provider's underlying data source
    461         insertData();
    462 
    463         // Gets all the columns for all the rows in the table
    464         cursor = mMockResolver.query(
    465             NotePad.Notes.CONTENT_URI,  // the URI for the main data table
    466             null,                       // no projection, get all columns
    467             null,                       // no selection criteria, get all records
    468             null,                       // no selection arguments
    469             null                        // use default sort order
    470         );
    471 
    472         // Asserts that the returned cursor contains the same number of rows as the size of the
    473         // test data array.
    474         assertEquals(TEST_NOTES.length, cursor.getCount());
    475 
    476         // Query subtest 3.
    477         // A query that uses a projection should return a cursor with the same number of columns
    478         // as the projection, with the same names, in the same order.
    479         Cursor projectionCursor = mMockResolver.query(
    480               NotePad.Notes.CONTENT_URI,  // the URI for the main data table
    481               TEST_PROJECTION,            // get the title, note, and mod date columns
    482               null,                       // no selection columns, get all the records
    483               null,                       // no selection criteria
    484               null                        // use default the sort order
    485         );
    486 
    487         // Asserts that the number of columns in the cursor is the same as in the projection
    488         assertEquals(TEST_PROJECTION.length, projectionCursor.getColumnCount());
    489 
    490         // Asserts that the names of the columns in the cursor and in the projection are the same.
    491         // This also verifies that the names are in the same order.
    492         assertEquals(TEST_PROJECTION[0], projectionCursor.getColumnName(0));
    493         assertEquals(TEST_PROJECTION[1], projectionCursor.getColumnName(1));
    494         assertEquals(TEST_PROJECTION[2], projectionCursor.getColumnName(2));
    495 
    496         // Query subtest 4
    497         // A query that uses selection criteria should return only those rows that match the
    498         // criteria. Use a projection so that it's easy to get the data in a particular column.
    499         projectionCursor = mMockResolver.query(
    500             NotePad.Notes.CONTENT_URI, // the URI for the main data table
    501             TEST_PROJECTION,           // get the title, note, and mod date columns
    502             SELECTION_COLUMNS,         // select on the title column
    503             SELECTION_ARGS,            // select titles "Note0", "Note1", or "Note5"
    504             SORT_ORDER                 // sort ascending on the title column
    505         );
    506 
    507         // Asserts that the cursor has the same number of rows as the number of selection arguments
    508         assertEquals(SELECTION_ARGS.length, projectionCursor.getCount());
    509 
    510         int index = 0;
    511 
    512         while (projectionCursor.moveToNext()) {
    513 
    514             // Asserts that the selection argument at the current index matches the value of
    515             // the title column (column 0) in the current record of the cursor
    516             assertEquals(SELECTION_ARGS[index], projectionCursor.getString(0));
    517 
    518             index++;
    519         }
    520 
    521         // Asserts that the index pointer is now the same as the number of selection arguments, so
    522         // that the number of arguments tested is exactly the same as the number of rows returned.
    523         assertEquals(SELECTION_ARGS.length, index);
    524 
    525     }
    526 
    527     /*
    528      * Tests queries against the provider, using the note id URI. This URI encodes a single
    529      * record ID. The provider should only return 0 or 1 record.
    530      */
    531     public void testQueriesOnNoteIdUri() {
    532       // Defines the selection column for a query. The "?" is replaced by entries in the
    533       // selection argument array
    534       final String SELECTION_COLUMNS = NotePad.Notes.COLUMN_NAME_TITLE + " = " + "?";
    535 
    536       // Defines the argument for the selection column.
    537       final String[] SELECTION_ARGS = { "Note1" };
    538 
    539       // A sort order for the query.
    540       final String SORT_ORDER = NotePad.Notes.COLUMN_NAME_TITLE + " ASC";
    541 
    542       // Creates a projection includes the note id column, so that note id can be retrieved.
    543       final String[] NOTE_ID_PROJECTION = {
    544            NotePad.Notes._ID,                 // The Notes class extends BaseColumns,
    545                                               // which includes _ID as the column name for the
    546                                               // record's id in the data model
    547            NotePad.Notes.COLUMN_NAME_TITLE};  // The note's title
    548 
    549       // Query subtest 1.
    550       // Tests that a query against an empty table returns null.
    551 
    552       // Constructs a URI that matches the provider's notes id URI pattern, using an arbitrary
    553       // value of 1 as the note ID.
    554       Uri noteIdUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_ID_URI_BASE, 1);
    555 
    556       // Queries the table with the notes ID URI. This should return an empty cursor.
    557       Cursor cursor = mMockResolver.query(
    558           noteIdUri, // URI pointing to a single record
    559           null,      // no projection, get all the columns for each record
    560           null,      // no selection criteria, get all the records in the table
    561           null,      // no need for selection arguments
    562           null       // default sort, by ascending title
    563       );
    564 
    565       // Asserts that the cursor is null.
    566       assertEquals(0,cursor.getCount());
    567 
    568       // Query subtest 2.
    569       // Tests that a query against a table containing records returns a single record whose ID
    570       // is the one requested in the URI provided.
    571 
    572       // Inserts the test data into the provider's underlying data source.
    573       insertData();
    574 
    575       // Queries the table using the URI for the full table.
    576       cursor = mMockResolver.query(
    577           NotePad.Notes.CONTENT_URI, // the base URI for the table
    578           NOTE_ID_PROJECTION,        // returns the ID and title columns of rows
    579           SELECTION_COLUMNS,         // select based on the title column
    580           SELECTION_ARGS,            // select title of "Note1"
    581           SORT_ORDER                 // sort order returned is by title, ascending
    582       );
    583 
    584       // Asserts that the cursor contains only one row.
    585       assertEquals(1, cursor.getCount());
    586 
    587       // Moves to the cursor's first row, and asserts that this did not fail.
    588       assertTrue(cursor.moveToFirst());
    589 
    590       // Saves the record's note ID.
    591       int inputNoteId = cursor.getInt(0);
    592 
    593       // Builds a URI based on the provider's content ID URI base and the saved note ID.
    594       noteIdUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_ID_URI_BASE, inputNoteId);
    595 
    596       // Queries the table using the content ID URI, which returns a single record with the
    597       // specified note ID, matching the selection criteria provided.
    598       cursor = mMockResolver.query(noteIdUri, // the URI for a single note
    599           NOTE_ID_PROJECTION,                 // same projection, get ID and title columns
    600           SELECTION_COLUMNS,                  // same selection, based on title column
    601           SELECTION_ARGS,                     // same selection arguments, title = "Note1"
    602           SORT_ORDER                          // same sort order returned, by title, ascending
    603       );
    604 
    605       // Asserts that the cursor contains only one row.
    606       assertEquals(1, cursor.getCount());
    607 
    608       // Moves to the cursor's first row, and asserts that this did not fail.
    609       assertTrue(cursor.moveToFirst());
    610 
    611       // Asserts that the note ID passed to the provider is the same as the note ID returned.
    612       assertEquals(inputNoteId, cursor.getInt(0));
    613     }
    614 
    615     /*
    616      *  Tests inserts into the data model.
    617      */
    618     public void testInserts() {
    619         // Creates a new note instance with ID of 30.
    620         NoteInfo note = new NoteInfo(
    621             "Note30", // the note's title
    622             "Test inserting a note" // the note's content
    623         );
    624 
    625         // Sets the note's creation and modification times
    626         note.setCreationDate(START_DATE + (10 * ONE_DAY_MILLIS));
    627         note.setModificationDate(START_DATE + (2 * ONE_WEEK_MILLIS));
    628 
    629         // Insert subtest 1.
    630         // Inserts a row using the new note instance.
    631         // No assertion will be done. The insert() method either works or throws an Exception
    632         Uri rowUri = mMockResolver.insert(
    633             NotePad.Notes.CONTENT_URI,  // the main table URI
    634             note.getContentValues()     // the map of values to insert as a new record
    635         );
    636 
    637         // Parses the returned URI to get the note ID of the new note. The ID is used in subtest 2.
    638         long noteId = ContentUris.parseId(rowUri);
    639 
    640         // Does a full query on the table. Since insertData() hasn't yet been called, the
    641         // table should only contain the record just inserted.
    642         Cursor cursor = mMockResolver.query(
    643             NotePad.Notes.CONTENT_URI, // the main table URI
    644             null,                      // no projection, return all the columns
    645             null,                      // no selection criteria, return all the rows in the model
    646             null,                      // no selection arguments
    647             null                       // default sort order
    648         );
    649 
    650         // Asserts that there should be only 1 record.
    651         assertEquals(1, cursor.getCount());
    652 
    653         // Moves to the first (and only) record in the cursor and asserts that this worked.
    654         assertTrue(cursor.moveToFirst());
    655 
    656         // Since no projection was used, get the column indexes of the returned columns
    657         int titleIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
    658         int noteIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
    659         int crdateIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_CREATE_DATE);
    660         int moddateIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE);
    661 
    662         // Tests each column in the returned cursor against the data that was inserted, comparing
    663         // the field in the NoteInfo object to the data at the column index in the cursor.
    664         assertEquals(note.title, cursor.getString(titleIndex));
    665         assertEquals(note.note, cursor.getString(noteIndex));
    666         assertEquals(note.createDate, cursor.getLong(crdateIndex));
    667         assertEquals(note.modDate, cursor.getLong(moddateIndex));
    668 
    669         // Insert subtest 2.
    670         // Tests that we can't insert a record whose id value already exists.
    671 
    672         // Defines a ContentValues object so that the test can add a note ID to it.
    673         ContentValues values = note.getContentValues();
    674 
    675         // Adds the note ID retrieved in subtest 1 to the ContentValues object.
    676         values.put(NotePad.Notes._ID, (int) noteId);
    677 
    678         // Tries to insert this record into the table. This should fail and drop into the
    679         // catch block. If it succeeds, issue a failure message.
    680         try {
    681             rowUri = mMockResolver.insert(NotePad.Notes.CONTENT_URI, values);
    682             fail("Expected insert failure for existing record but insert succeeded.");
    683         } catch (Exception e) {
    684           // succeeded, so do nothing.
    685         }
    686     }
    687 
    688     /*
    689      * Tests deletions from the data model.
    690      */
    691     public void testDeletes() {
    692         // Subtest 1.
    693         // Tries to delete a record from a data model that is empty.
    694 
    695         // Sets the selection column to "title"
    696         final String SELECTION_COLUMNS = NotePad.Notes.COLUMN_NAME_TITLE + " = " + "?";
    697 
    698         // Sets the selection argument "Note0"
    699         final String[] SELECTION_ARGS = { "Note0" };
    700 
    701         // Tries to delete rows matching the selection criteria from the data model.
    702         int rowsDeleted = mMockResolver.delete(
    703             NotePad.Notes.CONTENT_URI, // the base URI of the table
    704             SELECTION_COLUMNS,         // select based on the title column
    705             SELECTION_ARGS             // select title = "Note0"
    706         );
    707 
    708         // Assert that the deletion did not work. The number of deleted rows should be zero.
    709         assertEquals(0, rowsDeleted);
    710 
    711         // Subtest 2.
    712         // Tries to delete an existing record. Repeats the previous subtest, but inserts data first.
    713 
    714         // Inserts data into the model.
    715         insertData();
    716 
    717         // Uses the same parameters to try to delete the row with title "Note0"
    718         rowsDeleted = mMockResolver.delete(
    719             NotePad.Notes.CONTENT_URI, // the base URI of the table
    720             SELECTION_COLUMNS,         // same selection column, "title"
    721             SELECTION_ARGS             // same selection arguments, title = "Note0"
    722         );
    723 
    724         // The number of deleted rows should be 1.
    725         assertEquals(1, rowsDeleted);
    726 
    727         // Tests that the record no longer exists. Tries to get it from the table, and
    728         // asserts that nothing was returned.
    729 
    730         // Queries the table with the same selection column and argument used to delete the row.
    731         Cursor cursor = mMockResolver.query(
    732             NotePad.Notes.CONTENT_URI, // the base URI of the table
    733             null,                      // no projection, return all columns
    734             SELECTION_COLUMNS,         // select based on the title column
    735             SELECTION_ARGS,            // select title = "Note0"
    736             null                       // use the default sort order
    737         );
    738 
    739         // Asserts that the cursor is empty since the record had already been deleted.
    740         assertEquals(0, cursor.getCount());
    741     }
    742 
    743     /*
    744      * Tests updates to the data model.
    745      */
    746     public void testUpdates() {
    747         // Selection column for identifying a record in the data model.
    748         final String SELECTION_COLUMNS = NotePad.Notes.COLUMN_NAME_TITLE + " = " + "?";
    749 
    750         // Selection argument for the selection column.
    751         final String[] selectionArgs = { "Note1" };
    752 
    753         // Defines a map of column names and values
    754         ContentValues values = new ContentValues();
    755 
    756         // Subtest 1.
    757         // Tries to update a record in an empty table.
    758 
    759         // Sets up the update by putting the "note" column and a value into the values map.
    760         values.put(NotePad.Notes.COLUMN_NAME_NOTE, "Testing an update with this string");
    761 
    762         // Tries to update the table
    763         int rowsUpdated = mMockResolver.update(
    764             NotePad.Notes.CONTENT_URI,  // the URI of the data table
    765             values,                     // a map of the updates to do (column title and value)
    766             SELECTION_COLUMNS,           // select based on the title column
    767             selectionArgs               // select "title = Note1"
    768         );
    769 
    770         // Asserts that no rows were updated.
    771         assertEquals(0, rowsUpdated);
    772 
    773         // Subtest 2.
    774         // Builds the table, and then tries the update again using the same arguments.
    775 
    776         // Inserts data into the model.
    777         insertData();
    778 
    779         //  Does the update again, using the same arguments as in subtest 1.
    780         rowsUpdated = mMockResolver.update(
    781             NotePad.Notes.CONTENT_URI,   // The URI of the data table
    782             values,                      // the same map of updates
    783             SELECTION_COLUMNS,            // same selection, based on the title column
    784             selectionArgs                // same selection argument, to select "title = Note1"
    785         );
    786 
    787         // Asserts that only one row was updated. The selection criteria evaluated to
    788         // "title = Note1", and the test data should only contain one row that matches that.
    789         assertEquals(1, rowsUpdated);
    790 
    791     }
    792 
    793     // A utility for converting note data to a ContentValues map.
    794     private static class NoteInfo {
    795         String title;
    796         String note;
    797         long createDate;
    798         long modDate;
    799 
    800         /*
    801          * Constructor for a NoteInfo instance. This class helps create a note and
    802          * return its values in a ContentValues map expected by data model methods.
    803          * The note's id is created automatically when it is inserted into the data model.
    804          */
    805         public NoteInfo(String t, String n) {
    806             title = t;
    807             note = n;
    808             createDate = 0;
    809             modDate = 0;
    810         }
    811 
    812         // Sets the creation date for a test note
    813         public void setCreationDate(long c) {
    814             createDate = c;
    815         }
    816 
    817         // Sets the modification date for a test note
    818         public void setModificationDate(long m) {
    819             modDate = m;
    820         }
    821 
    822         /*
    823          * Returns a ContentValues instance (a map) for this NoteInfo instance. This is useful for
    824          * inserting a NoteInfo into a database.
    825          */
    826         public ContentValues getContentValues() {
    827             // Gets a new ContentValues object
    828             ContentValues v = new ContentValues();
    829 
    830             // Adds map entries for the user-controlled fields in the map
    831             v.put(NotePad.Notes.COLUMN_NAME_TITLE, title);
    832             v.put(NotePad.Notes.COLUMN_NAME_NOTE, note);
    833             v.put(NotePad.Notes.COLUMN_NAME_CREATE_DATE, createDate);
    834             v.put(NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE, modDate);
    835             return v;
    836 
    837         }
    838     }
    839 }
    840