CalendarDatabaseHelperTest.java revision 7b40dde3168f4af2c757cb43955aa3bfe1668666
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 */
16package com.android.providers.calendar;
17
18import android.content.ContentValues;
19import android.content.Context;
20import android.database.Cursor;
21import android.database.DatabaseUtils;
22import android.database.sqlite.SQLiteDatabase;
23import android.database.sqlite.SQLiteDatabase.CursorFactory;
24import android.provider.Calendar;
25import android.test.suitebuilder.annotation.MediumTest;
26import android.text.TextUtils;
27import android.util.Log;
28
29import junit.framework.TestCase;
30
31public class CalendarDatabaseHelperTest extends TestCase {
32
33    private SQLiteDatabase mBadDb;
34    private SQLiteDatabase mGoodDb;
35    private DatabaseUtils.InsertHelper mBadEventsInserter;
36    private DatabaseUtils.InsertHelper mGoodEventsInserter;
37
38    @Override
39    public void setUp() {
40        mBadDb = SQLiteDatabase.create(null);
41        assertNotNull(mBadDb);
42        mGoodDb = SQLiteDatabase.create(null);
43        assertNotNull(mGoodDb);
44    }
45
46    private void createVersion67EventsTable(SQLiteDatabase db) {
47        db.execSQL("CREATE TABLE Events (" +
48                "_id INTEGER PRIMARY KEY," +
49                "_sync_account TEXT," +
50                "_sync_account_type TEXT," +
51                "_sync_id TEXT," +
52                "_sync_version TEXT," +
53                "_sync_time TEXT," +            // UTC
54                "_sync_local_id INTEGER," +
55                "_sync_dirty INTEGER," +
56                "_sync_mark INTEGER," + // To filter out new rows
57                "calendar_id INTEGER NOT NULL," +
58                "htmlUri TEXT," +
59                "title TEXT," +
60                "eventLocation TEXT," +
61                "description TEXT," +
62                "eventStatus INTEGER," +
63                "selfAttendeeStatus INTEGER NOT NULL DEFAULT 0," +
64                "commentsUri TEXT," +
65                "dtstart INTEGER," +               // millis since epoch
66                "dtend INTEGER," +                 // millis since epoch
67                "eventTimezone TEXT," +         // timezone for event
68                "duration TEXT," +
69                "allDay INTEGER NOT NULL DEFAULT 0," +
70                "visibility INTEGER NOT NULL DEFAULT 0," +
71                "transparency INTEGER NOT NULL DEFAULT 0," +
72                "hasAlarm INTEGER NOT NULL DEFAULT 0," +
73                "hasExtendedProperties INTEGER NOT NULL DEFAULT 0," +
74                "rrule TEXT," +
75                "rdate TEXT," +
76                "exrule TEXT," +
77                "exdate TEXT," +
78                "originalEvent TEXT," +  // _sync_id of recurring event
79                "originalInstanceTime INTEGER," +  // millis since epoch
80                "originalAllDay INTEGER," +
81                "lastDate INTEGER," +               // millis since epoch
82                "hasAttendeeData INTEGER NOT NULL DEFAULT 0," +
83                "guestsCanModify INTEGER NOT NULL DEFAULT 0," +
84                "guestsCanInviteOthers INTEGER NOT NULL DEFAULT 1," +
85                "guestsCanSeeGuests INTEGER NOT NULL DEFAULT 1," +
86                "organizer STRING," +
87                "deleted INTEGER NOT NULL DEFAULT 0," +
88                "dtstart2 INTEGER," + //millis since epoch, allDay events in local timezone
89                "dtend2 INTEGER," + //millis since epoch, allDay events in local timezone
90                "eventTimezone2 TEXT," + //timezone for event with allDay events in local timezone
91                "syncAdapterData TEXT" + //available for use by sync adapters
92                ");");
93    }
94
95    private void addVersion67Events() {
96        // April 5th 1:01:01 AM to April 6th 1:01:01
97        mBadDb.execSQL("INSERT INTO Events (_id,dtstart,dtend,duration,dtstart2,dtend2," +
98                "eventTimezone,eventTimezone2,allDay,calendar_id) " +
99                "VALUES (1,1270454471000,1270540872000,'P10S'," +
100                "1270454460000,1270540861000,'America/Los_Angeles','America/Los_Angeles',1,1);");
101
102        // April 5th midnight to April 6th midnight, duration cleared
103        mGoodDb.execSQL("INSERT INTO Events (_id,dtstart,dtend,duration,dtstart2,dtend2," +
104                "eventTimezone,eventTimezone2,allDay,calendar_id) " +
105                "VALUES (1,1270450800000,1270537200000,null," +
106                "1270450800000,1270537200000,'America/Los_Angeles','America/Los_Angeles',1,1);");
107
108        // April 5th 1:01:01 AM to April 6th 1:01:01, recurring weekly (We only check for the
109        // existence of an rrule so it doesn't matter if the day is correct)
110        mBadDb.execSQL("INSERT INTO Events (_id,dtstart,dtend,duration,dtstart2,dtend2," +
111                "eventTimezone,eventTimezone2,allDay,rrule,calendar_id) " +
112                "VALUES (2,1270454462000,1270540863000," +
113                "'P10S',1270454461000,1270540861000,'America/Los_Angeles','America/Los_Angeles',1," +
114                "'WEEKLY:MON',1);");
115
116        // April 5th midnight with 1 day duration, if only dtend was wrong we wouldn't fix it, but
117        // if anything else is wrong we clear dtend to be sure.
118        mGoodDb.execSQL("INSERT INTO Events (" +
119                "_id,dtstart,dtend,duration,dtstart2,dtend2," +
120                "eventTimezone,eventTimezone2,allDay,rrule,calendar_id)" +
121                "VALUES (2,1270450800000,null,'P1D',1270450800000,null," +
122                "'America/Los_Angeles','America/Los_Angeles',1," +
123                "'WEEKLY:MON',1);");
124
125        assertEquals(mBadDb.rawQuery("SELECT _id FROM Events;", null).getCount(), 2);
126        assertEquals(mGoodDb.rawQuery("SELECT _id FROM Events;", null).getCount(), 2);
127    }
128
129    @MediumTest
130    public void testUpgradeToVersion68() {
131        // Create event tables
132        createVersion67EventsTable(mBadDb);
133        createVersion67EventsTable(mGoodDb);
134        // Fill in good and bad events
135        addVersion67Events();
136        // Run the upgrade on the bad events
137        CalendarDatabaseHelper.upgradeToVersion68(mBadDb);
138        Cursor badCursor = null;
139        Cursor goodCursor = null;
140        try {
141            badCursor = mBadDb.rawQuery("SELECT _id,dtstart,dtend,duration,dtstart2,dtend2," +
142                    "eventTimezone,eventTimezone2,rrule FROM Events WHERE allDay=?",
143                    new String[] {"1"});
144            goodCursor = mGoodDb.rawQuery("SELECT _id,dtstart,dtend,duration,dtstart2,dtend2," +
145                    "eventTimezone,eventTimezone2,rrule FROM Events WHERE allDay=?",
146                    new String[] {"1"});
147            // Check that we get the correct results back
148            assertTrue(compareCursors(badCursor, goodCursor));
149        } finally {
150            if (badCursor != null) {
151                badCursor.close();
152            }
153            if (goodCursor != null) {
154                goodCursor.close();
155            }
156        }
157    }
158
159    /**
160     * Compares two cursors to see if they contain the same data.
161     *
162     * @return Returns true of the cursors contain the same data and are not null, false
163     * otherwise
164     */
165    private static boolean compareCursors(Cursor c1, Cursor c2) {
166        if(c1 == null || c2 == null) {
167            Log.d("CDBT","c1 is " + c1 + " and c2 is " + c2);
168            return false;
169        }
170
171        int numColumns = c1.getColumnCount();
172        if (numColumns != c2.getColumnCount()) {
173            Log.d("CDBT","c1 has " + numColumns + " columns and c2 has " + c2.getColumnCount());
174            return false;
175        }
176
177        if (c1.getCount() != c2.getCount()) {
178            Log.d("CDBT","c1 has " + c1.getCount() + " rows and c2 has " + c2.getCount());
179            return false;
180        }
181
182        c1.moveToPosition(-1);
183        c2.moveToPosition(-1);
184        while(c1.moveToNext() && c2.moveToNext()) {
185            for(int i = 0; i < numColumns; i++) {
186                if(!TextUtils.equals(c1.getString(i),c2.getString(i))) {
187                    Log.d("CDBT", c1.getString(i) + "\n" + c2.getString(i));
188                    return false;
189                }
190            }
191        }
192
193        return true;
194    }
195}
196