1 /* 2 * Copyright (C) 2009 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.android.calendarcommon2; 18 19 import com.android.calendarcommon2.ICalendar; 20 import com.android.calendarcommon2.RecurrenceSet; 21 22 import android.content.ContentValues; 23 import android.test.suitebuilder.annotation.SmallTest; 24 import android.util.Log; 25 import android.provider.CalendarContract; 26 import junit.framework.TestCase; 27 28 import java.util.Arrays; 29 import java.util.List; 30 31 /** 32 * Test some pim.RecurrenceSet functionality. 33 */ 34 public class RecurrenceSetTest extends TestCase { 35 36 // Test a recurrence 37 @SmallTest testRecurrenceSet0()38 public void testRecurrenceSet0() throws Exception { 39 String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n" 40 + "DTEND;TZID=America/New_York:20080221T190000\n" 41 + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n" 42 + "EXDATE:20080222T120000Z"; 43 final ContentValues values = verifyPopulateContentValues(recurrence, 44 "FREQ=DAILY;UNTIL=20080222T000000Z", null, 45 null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0, false); 46 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 47 new String[] {"FREQ=DAILY;UNTIL=20080222T000000Z"}, null, 48 null, new Long[] {1203681600000L}); 49 } 50 51 // Test 1 day all-day event 52 @SmallTest testRecurrenceSet1()53 public void testRecurrenceSet1() throws Exception { 54 String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n" 55 + "RRULE:FREQ=YEARLY;WKST=SU"; 56 final ContentValues values = verifyPopulateContentValues(recurrence, 57 "FREQ=YEARLY;WKST=SU", null, null, null, 1250812800000L, "UTC", "P1D", 1, false); 58 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 59 new String[] {"FREQ=YEARLY;WKST=SU"}, null, null, null); 60 } 61 62 // Test 2 day all-day event 63 @SmallTest testRecurrenceSet2()64 public void testRecurrenceSet2() throws Exception { 65 String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n" 66 + "RRULE:FREQ=YEARLY;WKST=SU"; 67 final ContentValues values = verifyPopulateContentValues(recurrence, 68 "FREQ=YEARLY;WKST=SU", null, null, null, 1250812800000L, "UTC", "P2D", 1, false); 69 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 70 new String[] {"FREQ=YEARLY;WKST=SU"}, null, null, null); 71 } 72 73 // Test multi-rule RRULE. 74 @SmallTest testRecurrenceSet3()75 public void testRecurrenceSet3() throws Exception { 76 String recurrence = "DTSTART;VALUE=DATE:20090821\n" 77 + "RRULE:FREQ=YEARLY;WKST=SU\n" 78 + "RRULE:FREQ=MONTHLY;COUNT=3\n" 79 + "DURATION:P2H"; 80 final ContentValues values = verifyPopulateContentValues(recurrence, 81 "FREQ=YEARLY;WKST=SU\nFREQ=MONTHLY;COUNT=3", null, 82 null, null, 1250812800000L, "UTC", "P2H", 1 /*allDay*/, false); 83 // allDay=1 just means the start time is 00:00:00 UTC. 84 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 85 new String[] {"FREQ=YEARLY;WKST=SU", "FREQ=MONTHLY;COUNT=3"}, 86 null, null, null); 87 } 88 89 // Test RDATE with VALUE=DATE. 90 @SmallTest testRecurrenceSet4()91 public void testRecurrenceSet4() throws Exception { 92 String recurrence = "DTSTART;TZID=America/Los_Angeles:20090821T010203\n" 93 + "RDATE;TZID=America/Los_Angeles;VALUE=DATE:20110601,20110602,20110603\n" 94 + "DURATION:P2H"; 95 final ContentValues values = verifyPopulateContentValues(recurrence, null, 96 //"TZID=America/Los_Angeles;VALUE=DATE:20110601,20110602,20110603", 97 "America/Los_Angeles;20110601,20110602,20110603", // incorrect 98 null, null, 1250841723000L, "America/Los_Angeles", "P2H", 0 /*allDay*/, false); 99 // allDay=1 just means the start time is 00:00:00 UTC. 100 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 101 null, new Long[] {1306911600000L, 1306998000000L, 1307084400000L}, null, null); 102 } 103 104 // Check generation of duration from events in different time zones. 105 @SmallTest testRecurrenceSet5()106 public void testRecurrenceSet5() throws Exception { 107 String recurrence = "DTSTART;TZID=America/Los_Angeles:20090821T070000\n" 108 + "DTEND;TZID=America/New_York:20090821T110000\n" 109 + "RRULE:FREQ=YEARLY\n"; 110 final ContentValues values = verifyPopulateContentValues(recurrence, "FREQ=YEARLY", null, 111 null, null, 1250863200000L, "America/Los_Angeles", "P3600S" /*P1H*/, 0 /*allDay*/, 112 false); 113 // TODO: would like to use P1H for duration 114 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 115 new String[] {"FREQ=YEARLY"}, null, null, null); 116 117 String recurrence2 = "DTSTART;TZID=America/New_York:20090821T100000\n" 118 + "DTEND;TZID=America/Los_Angeles:20090821T080000\n" 119 + "RRULE:FREQ=YEARLY\n"; 120 final ContentValues values2 = verifyPopulateContentValues(recurrence2, "FREQ=YEARLY", null, 121 null, null, 1250863200000L, "America/New_York", "P3600S" /*P1H*/, 0 /*allDay*/, 122 false); 123 // TODO: should we rigorously define which tzid becomes the "event timezone"? 124 verifyRecurrenceSetInitialization(new RecurrenceSet(values2), 125 new String[] {"FREQ=YEARLY"}, null, null, null); 126 } 127 128 // Test multi-rule EXRULE. 129 @SmallTest testRecurrenceSet6()130 public void testRecurrenceSet6() throws Exception { 131 final String recurrence = "DTSTART;VALUE=DATE:20090821\n" 132 + "RRULE:FREQ=YEARLY;WKST=SU\n" 133 + "RRULE:FREQ=MONTHLY;COUNT=6\n" 134 + "EXRULE:FREQ=YEARLY;INTERVAL=4\n" 135 + "EXRULE:FREQ=MONTHLY;INTERVAL=2\n" 136 + "EXDATE:20120821\n" 137 + "DURATION:P2H"; 138 final ContentValues values = verifyPopulateContentValues(recurrence, 139 "FREQ=YEARLY;WKST=SU\nFREQ=MONTHLY;COUNT=6", null, 140 "FREQ=YEARLY;INTERVAL=4\nFREQ=MONTHLY;INTERVAL=2", 141 "20120821", 1250812800000L, "UTC", "P2H", 1, false); 142 verifyRecurrenceSetInitialization(new RecurrenceSet(values), 143 new String[] {"FREQ=YEARLY;WKST=SU", "FREQ=MONTHLY;COUNT=6"}, null, 144 new String[] {"FREQ=YEARLY;INTERVAL=4", "FREQ=MONTHLY;INTERVAL=2"}, 145 new Long[] {1345507200000L}); 146 } 147 148 // Test multi-rule RDATE and EXDATE. 149 @SmallTest testRecurrentSet7()150 public void testRecurrentSet7() throws Exception { 151 final RecurrenceSet rs = new RecurrenceSet( 152 "FREQ=YEARLY;WKST=SU", 153 "America/Los_Angeles;20110601,20110602\n20110603T120000Z", 154 "FREQ=YEARLY;INTERVAL=4", 155 "America/New_York;20120601,20120602\n20120603T120000Z"); 156 verifyRecurrenceSetInitialization(rs, 157 new String[] {"FREQ=YEARLY;WKST=SU"}, 158 new Long[] {1306911600000L, 1306998000000L, 1307102400000L}, 159 new String[] {"FREQ=YEARLY;INTERVAL=4"}, 160 new Long[] {1338523200000L, 1338609600000L, 1338724800000L}); 161 } 162 163 // Test a failure to parse the recurrence data 164 @SmallTest testRecurrenceSetBadDstart()165 public void testRecurrenceSetBadDstart() throws Exception { 166 String recurrence = "DTSTART;TZID=GMT+05:30:20080221T070000\n" 167 + "DTEND;TZID=GMT+05:30:20080221T190000\n" 168 + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n" 169 + "EXDATE:20080222T120000Z"; 170 verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null, 171 null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0, true); 172 } 173 174 @SmallTest testRecurrenceSetBadRrule()175 public void testRecurrenceSetBadRrule() throws Exception { 176 String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n" 177 + "DTEND;TZID=GMT+05:30:20080221T190000\n" 178 + "RRULE:FREQ=NEVER;UNTIL=20080222T000000Z\n" 179 + "EXDATE:20080222T120000Z"; 180 verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null, 181 null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0, true); 182 } 183 verifyRecurrenceSetInitialization(RecurrenceSet rs, String[] expectedRruleStrs, Long[] expectedRdates, String[] expectedExruleStrs, Long[] expectedExdates)184 private void verifyRecurrenceSetInitialization(RecurrenceSet rs, 185 String[] expectedRruleStrs, Long[] expectedRdates, 186 String[] expectedExruleStrs, Long[] expectedExdates) { 187 verify(convertToEventRecurrences(expectedRruleStrs), rs.rrules); 188 verify(expectedRdates, convertToLong(rs.rdates)); 189 verify(convertToEventRecurrences(expectedExruleStrs), rs.exrules); 190 verify(expectedExdates, convertToLong(rs.exdates)); 191 } 192 convertToEventRecurrences(String[] ruleStrs)193 private EventRecurrence[] convertToEventRecurrences(String[] ruleStrs) { 194 if (ruleStrs == null) { 195 return null; 196 } 197 final EventRecurrence[] rules = new EventRecurrence[ruleStrs.length]; 198 for (int i = 0; i < ruleStrs.length; ++i) { 199 rules[i] = new EventRecurrence(); 200 rules[i].parse(ruleStrs[i]); 201 } 202 return rules; 203 } 204 convertToLong(long[] primitives)205 private Long[] convertToLong(long[] primitives) { 206 if (primitives == null) { 207 return null; 208 } 209 final Long[] datesLong = new Long[primitives.length]; 210 for (int i = 0; i < primitives.length; ++i) { 211 datesLong[i] = primitives[i]; 212 } 213 return datesLong; 214 } 215 verify(Object[] expected, Object[] actual)216 private void verify(Object[] expected, Object[] actual) { 217 if (actual == null && expected == null) { 218 return; 219 } 220 assertNotNull("actual result is null but expected is not. Expected: " 221 + Arrays.toString(expected), actual); 222 assertNotNull("expected result is null but actual is not. Actual: " 223 + Arrays.toString(actual), expected); 224 assertEquals("Expected and actual are not of same size." 225 + "Expected: " + Arrays.toString(expected) + " Actual: " + Arrays.toString(actual), 226 expected.length, actual.length); 227 List<Object> actualList = Arrays.asList(actual); 228 for (int i = 0; i < expected.length; ++i) { 229 if (!actualList.contains(expected[i])) { 230 fail("Expected: " + expected[i] + " but not found in Actual: " 231 + Arrays.toString(actual)); 232 } 233 } 234 } 235 236 // run populateContentValues and verify the results verifyPopulateContentValues(String recurrence, String rrule, String rdate, String exrule, String exdate, long dtstart, String tzid, String duration, int allDay, boolean badFormat)237 private ContentValues verifyPopulateContentValues(String recurrence, String rrule, String rdate, 238 String exrule, String exdate, long dtstart, String tzid, String duration, int allDay, 239 boolean badFormat) 240 throws ICalendar.FormatException { 241 ICalendar.Component recurrenceComponent = 242 new ICalendar.Component("DUMMY", null /* parent */); 243 ICalendar.parseComponent(recurrenceComponent, recurrence); 244 ContentValues values = new ContentValues(); 245 boolean result = RecurrenceSet.populateContentValues(recurrenceComponent, values); 246 Log.d("KS", "values " + values); 247 248 if (badFormat) { 249 assertEquals(result, !badFormat); 250 return null; 251 } 252 assertEquals(rrule, values.get(android.provider.CalendarContract.Events.RRULE)); 253 assertEquals(rdate, values.get(android.provider.CalendarContract.Events.RDATE)); 254 assertEquals(exrule, values.get(android.provider.CalendarContract.Events.EXRULE)); 255 assertEquals(exdate, values.get(android.provider.CalendarContract.Events.EXDATE)); 256 assertEquals(dtstart, (long) values.getAsLong(CalendarContract.Events.DTSTART)); 257 assertEquals(tzid, values.get(android.provider.CalendarContract.Events.EVENT_TIMEZONE)); 258 assertEquals(duration, values.get(android.provider.CalendarContract.Events.DURATION)); 259 assertEquals(allDay, 260 (int) values.getAsInteger(android.provider.CalendarContract.Events.ALL_DAY)); 261 return values; 262 } 263 264 } 265