FileDocCategorySizeDatePackage
CalendarProviderTest.javaAPI DocAndroid 1.5 API60460Wed May 06 22:42:48 BST 2009com.android.providers.calendar

CalendarProviderTest

public class CalendarProviderTest extends android.test.ProviderTestCase2
Runs various tests on an isolated Calendar provider with its own database.

Fields Summary
static final String
TAG
static final String
DEFAULT_TIMEZONE
private android.database.sqlite.SQLiteDatabase
mDb
private MetaData
mMetaData
private android.content.Context
mContext
private android.test.mock.MockContentResolver
mResolver
private android.net.Uri
mEventsUri
private int
mCalendarId
private int
mGlobalSyncId
private EventInfo[]
mEvents
This is the main table of events. The events in this table are referred to by name in other places.
private InstanceInfo[]
mInstanceRanges
This table is used to create repeating events and then check that the number of instances within a given range matches the expected number of instances.
private EventInfo[]
mBusyBitEvents
This tables of events is used to test the BusyBit database table.
private EventInfo[]
mBusyBitRepeatingEvents
private BusyBitInfo[]
mBusyBitTests
private Command[]
mNormalInsertDelete
This sequence of commands inserts and deletes some events.
private Command[]
mAlldayInsertDelete
This sequence of commands inserts and deletes some all-day events.
private Command[]
mRecurringInsertDelete
This sequence of commands inserts and deletes some repeating events.
private Command[]
mExceptionWithMovedRecurrence
This sequence of commands creates a recurring event with a recurrence exception that moves an event outside the expansion window. It checks that the recurrence exception does not occur in the Instances database table. Bug 1642665
private Command[]
mExceptionWithTruncatedRecurrence
This sequence of commands creates a recurring event with a recurrence exception and then changes the end time of the recurring event. It then checks that the recurrence exception does not occur in the Instances database table.
private Command[]
mExceptionWithNoRecurrence
Bug 135848. Ensure that a recurrence exception is displayed even if the recurrence is not present.
Constructors Summary
public CalendarProviderTest()

        super(CalendarProvider.class, Calendar.AUTHORITY);
    
Methods Summary
private voiddeleteAllEvents()

        mDb.execSQL("DELETE FROM Events;");
        mMetaData.clearInstanceRange();
    
private intdeleteMatchingEvents(java.lang.String title)
Deletes all the events that match the given title.

param
title the given title to match events on
return
the number of rows deleted

        Cursor cursor = mResolver.query(mEventsUri, new String[] { Events._ID },
                "title=?", new String[] { title }, null);
        int numRows = 0;
        while (cursor.moveToNext()) {
            long id = cursor.getLong(0);
            Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, id);
            numRows += mResolver.delete(uri, null, null);
        }
        cursor.close();
        return numRows;
    
private voiddumpCursor(android.database.Cursor cursor)
Dumps the contents of the given cursor to the log. For debugging.

param
cursor the database cursor

        String[] cols = cursor.getColumnNames();

        Log.i(TAG, "dumpCursor() count: " + cursor.getCount());
        int index = 0;
        while (cursor.moveToNext()) {
            Log.i(TAG, index + " {");
            for (int i = 0; i < cols.length; i++) {
                Log.i(TAG, "    " + cols[i] + '=" + cursor.getString(i));
            }
            Log.i(TAG, "}");
            index += 1;
        }
    
private com.android.providers.calendar.CalendarProviderTest$EventInfofindEvent(java.lang.String name)

    
        
        int len = mEvents.length;
        for (int ii = 0; ii < len; ii++) {
            EventInfo event = mEvents[ii];
            if (name.equals(event.mTitle)) {
                return event;
            }
        }
        return null;
    
private intinsertCal(java.lang.String name, java.lang.String timezone)

        ContentValues m = new ContentValues();
        m.put(Calendars.NAME, name);
        m.put(Calendars.DISPLAY_NAME, name);
        m.put(Calendars.COLOR, "0xff123456");
        m.put(Calendars.TIMEZONE, timezone);
        m.put(Calendars.SELECTED, 1);
        Uri url = mResolver.insert(Uri.parse("content://calendar/calendars"), m);
        String id = url.getLastPathSegment();
        return Integer.parseInt(id);
    
private android.net.UriinsertEvent(int calId, com.android.providers.calendar.CalendarProviderTest$EventInfo event)

        ContentValues m = new ContentValues();
        m.put(Events.CALENDAR_ID, calId);
        m.put(Events.TITLE, event.mTitle);
        m.put(Events.DTSTART, event.mDtstart);
        m.put(Events.ALL_DAY, event.mAllDay ? 1 : 0);
        
        if (event.mRrule == null) {
            // This is a normal event
            m.put(Events.DTEND, event.mDtend);
        } else {
            // This is a repeating event
            m.put(Events.RRULE, event.mRrule);
            m.put(Events.DURATION, event.mDuration);
        }
        
        if (event.mDescription != null) {
            m.put(Events.DESCRIPTION, event.mDescription);
        }
        if (event.mTimezone != null) {
            m.put(Events.EVENT_TIMEZONE, event.mTimezone);
        }
        
        if (event.mOriginalTitle != null) {
            // This is a recurrence exception.
            EventInfo recur = findEvent(event.mOriginalTitle);
            assertNotNull(recur);
            String syncId = String.format("%d", recur.mSyncId);
            m.put(Events.ORIGINAL_EVENT, syncId);
            m.put(Events.ORIGINAL_ALL_DAY, recur.mAllDay ? 1 : 0);
            m.put(Events.ORIGINAL_INSTANCE_TIME, event.mOriginalInstance);
        }
        Uri url = mResolver.insert(mEventsUri, m);
        
        // Create a fake _sync_id and add it to the event.  Update the database
        // directly so that we don't trigger any validation checks in the
        // CalendarProvider.
        long id = ContentUris.parseId(url);
        mDb.execSQL("UPDATE Events SET _sync_id=" + mGlobalSyncId + " WHERE _id=" + id);
        event.mSyncId = mGlobalSyncId;
        mGlobalSyncId += 1;

        return url;
    
private android.database.CursorqueryBusyBits(int startDay, int numDays)

        int endDay = startDay + numDays - 1;
        Uri url = Uri.parse("content://calendar/busybits/when/" + startDay + "/" + endDay);
        return mResolver.query(url, null, null, null, null);
    
private android.database.CursorqueryInstances(long begin, long end)

        Uri url = Uri.parse("content://calendar/instances/when/" + begin + "/" + end);
        return mResolver.query(url, null, null, null, null);
    
protected voidsetUp()

        super.setUp();

        mContext = getMockContext();
        mResolver = getMockContentResolver();
        mResolver.addProvider("subscribedfeeds", new MockProvider("subscribedfeeds"));
        mResolver.addProvider("sync", new MockProvider("sync"));

        mDb = getProvider().getDatabase();
        mMetaData = getProvider().mMetaData;
    
protected voidtearDown()

        super.tearDown();
    
public voidtestBusyBitRange()

        Cursor cursor;
        Uri url = null;

        int calId = insertCal("Calendar0", "America/Los_Angeles");

        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(0, cursor.getCount());
        cursor.close();

        int len = mBusyBitTests.length;
        for (int ii = 0; ii < len; ii++) {
            deleteAllEvents();
            BusyBitInfo busyInfo = mBusyBitTests[ii];
            EventInfo[] events = busyInfo.mEvents;
            int numEvents = events.length;
            for (int jj = 0; jj < numEvents; jj++) {
                EventInfo event = events[jj];
                insertEvent(calId, event);
            }
            
            int startDay = busyInfo.mStartDay;
            int numDays = busyInfo.mNumDays;
            int[] busybits = new int[numDays];
            int[] allDayCounts = new int[numDays];
            
            if (false) {
                cursor = mResolver.query(mEventsUri, null, null, null, null);
                Log.i(TAG, "Dump of Events table, count: " + cursor.getCount());
                dumpCursor(cursor);
                cursor.close();

                Time time = new Time();
                time.setJulianDay(startDay);
                long begin = time.toMillis(true);
                int endDay = startDay + numDays - 1;
                time.setJulianDay(endDay);
                long end = time.toMillis(true);
                cursor = queryInstances(begin, end);
                Log.i(TAG, "Dump of Instances table, count: " + cursor.getCount()
                        + " startDay: " + startDay + " endDay: " + endDay
                        + " begin: " + begin + " end: " + end);
                dumpCursor(cursor);
                cursor.close();
            }
            
            cursor = queryBusyBits(startDay, numDays);
            try {
                int dayColumnIndex = cursor.getColumnIndexOrThrow(BusyBits.DAY);
                int busybitColumnIndex = cursor.getColumnIndexOrThrow(BusyBits.BUSYBITS);
                int allDayCountColumnIndex = cursor.getColumnIndexOrThrow(BusyBits.ALL_DAY_COUNT);
                
                while (cursor.moveToNext()) {
                    int day = cursor.getInt(dayColumnIndex);
                    int dayIndex = day - startDay;
                    busybits[dayIndex] = cursor.getInt(busybitColumnIndex);
                    allDayCounts[dayIndex] = cursor.getInt(allDayCountColumnIndex);
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
            
            // Compare the database busy bits with the expected busy bits
            for (int dayIndex = 0; dayIndex < numDays; dayIndex++) {
                if (busyInfo.mBusyBits[dayIndex] != busybits[dayIndex]) {
                    String mesg = String.format("Test failed!"
                            + " BusyBit test index: %d"
                            + " day index: %d"
                            + " mStartDay: %d mNumDays: %d"
                            + " expected busybits: 0x%x was: 0x%x",
                            ii, dayIndex, busyInfo.mStartDay, busyInfo.mNumDays,
                            busyInfo.mBusyBits[dayIndex], busybits[dayIndex]);
                    Log.e(TAG, mesg);
                    
                    cursor = mResolver.query(mEventsUri, null, null, null, null);
                    Log.i(TAG, "Dump of Events table, count: " + cursor.getCount());
                    dumpCursor(cursor);
                    cursor.close();
                }
                assertEquals(busyInfo.mBusyBits[dayIndex], busybits[dayIndex]);
            }
            
            // Compare the database all-day counts with the expected all-day counts
            for (int dayIndex = 0; dayIndex < numDays; dayIndex++) {
                if (busyInfo.mAllDayCounts[dayIndex] != allDayCounts[dayIndex]) {
                    String mesg = String.format("Test failed!"
                            + " BusyBit test index: %d"
                            + " day index: %d"
                            + " expected all-day count: %d was: %d",
                            ii, dayIndex,
                            busyInfo.mAllDayCounts[dayIndex], allDayCounts[dayIndex]);
                    Log.e(TAG, mesg);
                }
                assertEquals(busyInfo.mAllDayCounts[dayIndex], allDayCounts[dayIndex]);
            }
        }
    
public voidtestCommandSequences()

        Cursor cursor;
        Uri url = null;

        mCalendarId = insertCal("Calendar0", DEFAULT_TIMEZONE);

        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(0, cursor.getCount());
        cursor.close();

        Log.i(TAG, "Normal insert/delete");
        Command[] commands = mNormalInsertDelete;
        for (Command command : commands) {
            command.execute();
        }
        
        deleteAllEvents();
        
        Log.i(TAG, "All-day insert/delete");
        commands = mAlldayInsertDelete;
        for (Command command : commands) {
            command.execute();
        }
        
        deleteAllEvents();
        
        Log.i(TAG, "Recurring insert/delete");
        commands = mRecurringInsertDelete;
        for (Command command : commands) {
            command.execute();
        }
        
        deleteAllEvents();

        Log.i(TAG, "Exception with truncated recurrence");
        commands = mExceptionWithTruncatedRecurrence;
        for (Command command : commands) {
            command.execute();
        }

        deleteAllEvents();

        Log.i(TAG, "Exception with moved recurrence");
        commands = mExceptionWithMovedRecurrence;
        for (Command command : commands) {
            command.execute();
        }

        deleteAllEvents();

        Log.i(TAG, "Exception with no recurrence");
        commands = mExceptionWithNoRecurrence;
        for (Command command : commands) {
            command.execute();
        }
    
public voidtestInsertNormalEvents()

        Cursor cursor;
        Uri url = null;

        int calId = insertCal("Calendar0", DEFAULT_TIMEZONE);

        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(0, cursor.getCount());
        cursor.close();

        // Keep track of the number of normal events
        int numEvents = 0;
        
        // "begin" is the earliest start time of all the normal events,
        // and "end" is the latest end time of all the normal events.
        long begin = 0, end = 0;
        
        int len = mEvents.length;
        for (int ii = 0; ii < len; ii++) {
            EventInfo event = mEvents[ii];
            // Skip repeating events and recurrence exceptions
            if (event.mRrule != null || event.mOriginalTitle != null) {
                continue;
            }
            if (numEvents == 0) {
                begin = event.mDtstart;
                end = event.mDtend;
            } else {
                if (begin > event.mDtstart) {
                    begin = event.mDtstart;
                }
                if (end < event.mDtend) {
                    end = event.mDtend;
                }
            }
            url = insertEvent(calId, event);
            numEvents += 1;
        }

        // query one
        cursor = mResolver.query(url, null, null, null, null);
        assertEquals(1, cursor.getCount());
        cursor.close();

        // query all
        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(numEvents, cursor.getCount());
        cursor.close();
        
        // Check that the Instances table has one instance of each of the
        // normal events.
        cursor = queryInstances(begin, end);
        assertEquals(numEvents, cursor.getCount());
        cursor.close();
    
public voidtestInsertRepeatingEvents()

        Cursor cursor;
        Uri url = null;

        int calId = insertCal("Calendar0", "America/Los_Angeles");

        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(0, cursor.getCount());
        cursor.close();

        // Keep track of the number of repeating events
        int numEvents = 0;

        int len = mEvents.length;
        for (int ii = 0; ii < len; ii++) {
            EventInfo event = mEvents[ii];
            // Skip normal events
            if (event.mRrule == null) {
                continue;
            }
            url = insertEvent(calId, event);
            numEvents += 1;
        }

        // query one
        cursor = mResolver.query(url, null, null, null, null);
        assertEquals(1, cursor.getCount());
        cursor.close();

        // query all
        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(numEvents, cursor.getCount());
        cursor.close();
    
public voidtestInstanceRange()

        Cursor cursor;
        Uri url = null;

        int calId = insertCal("Calendar0", "America/Los_Angeles");

        cursor = mResolver.query(mEventsUri, null, null, null, null);
        assertEquals(0, cursor.getCount());
        cursor.close();

        int len = mInstanceRanges.length;
        for (int ii = 0; ii < len; ii++) {
            InstanceInfo instance = mInstanceRanges[ii];
            EventInfo event = instance.mEvent;
            url = insertEvent(calId, event);
            cursor = queryInstances(instance.mBegin, instance.mEnd);
            if (instance.mExpectedOccurrences != cursor.getCount()) {
                Log.e(TAG, "Test failed! Instance index: " + ii);
                Log.e(TAG, "title: " + event.mTitle + " desc: " + event.mDescription
                        + " [begin,end]: [" + instance.mBegin + " " + instance.mEnd + "]"
                        + " expected: " + instance.mExpectedOccurrences);
                dumpCursor(cursor);
            }
            assertEquals(instance.mExpectedOccurrences, cursor.getCount());
            cursor.close();
            int rows = mResolver.delete(url, null /* selection */, null /* selection args */);
            assertEquals(1, rows);
        }
    
private intupdateMatchingEvents(java.lang.String title, android.content.ContentValues values)
Updates all the events that match the given title.

param
title the given title to match events on
return
the number of rows updated

        String[] projection = new String[] {
                Events._ID,
                Events.DTSTART,
                Events.DTEND,
                Events.DURATION,
                Events.ALL_DAY,
                Events.RRULE,
                Events.EVENT_TIMEZONE,
        };
        Cursor cursor = mResolver.query(mEventsUri, projection,
                "title=?", new String[] { title }, null);
        int numRows = 0;
        while (cursor.moveToNext()) {
            long id = cursor.getLong(0);
            
            // If any of the following fields are being changed, then we need
            // to include all of them.
            if (values.containsKey(Events.DTSTART) || values.containsKey(Events.DTEND)
                    || values.containsKey(Events.DURATION) || values.containsKey(Events.ALL_DAY)
                    || values.containsKey(Events.RRULE)
                    || values.containsKey(Events.EVENT_TIMEZONE)) {
                long dtstart = cursor.getLong(1);
                long dtend = cursor.getLong(2);
                String duration = cursor.getString(3);
                boolean allDay = cursor.getInt(4) != 0;
                String rrule = cursor.getString(5);
                String timezone = cursor.getString(6);
                
                if (!values.containsKey(Events.DTSTART)) {
                    values.put(Events.DTSTART, dtstart);
                }
                // Don't add DTEND for repeating events
                if (!values.containsKey(Events.DTEND) && rrule == null) {
                    values.put(Events.DTEND, dtend);
                }
                if (!values.containsKey(Events.DURATION) && duration != null) {
                    values.put(Events.DURATION, duration);
                }
                if (!values.containsKey(Events.ALL_DAY)) {
                    values.put(Events.ALL_DAY, allDay ? 1 : 0);
                }
                if (!values.containsKey(Events.RRULE) && rrule != null) {
                    values.put(Events.RRULE, rrule);
                }
                if (!values.containsKey(Events.EVENT_TIMEZONE) && timezone != null) {
                    values.put(Events.EVENT_TIMEZONE, timezone);
                }
            }
            
            Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, id);
            numRows += mResolver.update(uri, values, null, null);
        }
        cursor.close();
        return numRows;