1 /* 2 ** 3 ** Copyright 2009, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 /* Based on http://code.google.com/p/google-rfc-2445/source/browse/trunk/test/com/google/ical/iter/RRuleIteratorImplTest.java */ 19 20 package com.android.calendarcommon2; 21 22 import com.android.calendarcommon2.RecurrenceSet; 23 24 import android.os.Debug; 25 import android.test.suitebuilder.annotation.MediumTest; 26 import android.test.suitebuilder.annotation.Suppress; 27 import android.text.format.Time; 28 import junit.framework.TestCase; 29 30 /** 31 * You can run those tests with: 32 * 33 * adb shell am instrument 34 * -e debug false 35 * -w 36 * -e 37 * class com.android.providers.calendar.RRuleTest 38 * com.android.providers.calendar.tests/android.test.InstrumentationTestRunner 39 */ 40 41 public class RRuleTest extends TestCase { 42 private static final String TAG = "RRuleTest"; 43 private static final boolean METHOD_TRACE = false; 44 getFormattedDates(long[] dates, Time time, boolean truncate)45 private static String[] getFormattedDates(long[] dates, Time time, boolean truncate) { 46 String[] out = new String[dates.length]; 47 int i = 0; 48 for (long date : dates) { 49 time.set(date); 50 if (truncate) { 51 out[i] = time.format2445().substring(0, 8); // Just YYMMDD 52 } else { 53 out[i] = time.format2445().substring(0, 15); // YYMMDDThhmmss 54 } 55 ++i; 56 } 57 return out; 58 } 59 60 static final String PST = "America/Los_Angeles"; 61 static final String UTC = "UTC"; 62 // Use this date as end of recurrence unlessotherwise specified. 63 static final String DEFAULT_END = "20091212"; 64 runRecurrenceIteratorTest(String rruleText, String dtStart, int limit, String golden)65 private void runRecurrenceIteratorTest(String rruleText, String dtStart, int limit, 66 String golden) throws Exception { 67 runRecurrenceIteratorTest(rruleText, dtStart, limit, golden, null, null, UTC); 68 } 69 runRecurrenceIteratorTest(String rrule, String dtstartStr, int limit, String golden, String advanceTo, String tz)70 private void runRecurrenceIteratorTest(String rrule, String dtstartStr, int limit, 71 String golden, String advanceTo, String tz) throws Exception { 72 runRecurrenceIteratorTest(rrule, dtstartStr, limit, golden, advanceTo, null, tz); 73 } 74 75 /** 76 * Tests a recurrence rule 77 * @param rrule The rule to expand 78 * @param dtstartStr The dtstart to use 79 * @param limit Maximum number of entries to expand. if there are more, "..." is appended to 80 * the result. Note that Android's recurrence expansion doesn't support expanding n results, 81 * so this is faked by expanding until the endAt date, and then taking limit results. 82 * @param golden The desired results 83 * @param advanceTo The starting date for expansion. dtstartStr is used if null is passed in. 84 * @param endAt The ending date. DEFAULT_END is used if null is passed in. 85 * @param tz The time zone. UTC is used if null is passed in. 86 * @throws Exception if anything goes wrong. 87 */ runRecurrenceIteratorTest(String rrule, String dtstartStr, int limit, String golden, String advanceTo, String endAt, String tz)88 private void runRecurrenceIteratorTest(String rrule, String dtstartStr, int limit, 89 String golden, String advanceTo, String endAt, String tz) throws Exception { 90 91 String rdate = ""; 92 String exrule = ""; 93 String exdate = ""; 94 rrule = rrule.replace("RRULE:", ""); 95 // RecurrenceSet does not support folding of lines, so fold here 96 rrule = rrule.replace("\n ", ""); 97 98 Time dtstart = new Time(tz); 99 Time rangeStart = new Time(tz); 100 Time rangeEnd = new Time(tz); 101 Time outCal = new Time(tz); 102 103 dtstart.parse(dtstartStr); 104 if (advanceTo == null) { 105 advanceTo = dtstartStr; 106 } 107 if (endAt == null) { 108 endAt = DEFAULT_END; 109 } 110 111 rangeStart.parse(advanceTo); 112 rangeEnd.parse(endAt); 113 114 115 RecurrenceProcessor rp = new RecurrenceProcessor(); 116 RecurrenceSet recur = new RecurrenceSet(rrule, rdate, exrule, exdate); 117 118 long[] out = rp.expand(dtstart, recur, rangeStart.toMillis(false /* use isDst */), 119 rangeEnd.toMillis(false /* use isDst */)); 120 121 if (METHOD_TRACE) { 122 Debug.stopMethodTracing(); 123 } 124 125 boolean truncate = dtstartStr.length() <= 8; // Just date, not date-time 126 String[] actual = getFormattedDates(out, outCal, truncate); 127 128 StringBuilder sb = new StringBuilder(); 129 int k = 0; 130 while (k < actual.length && --limit >= 0) { 131 if (k != 0) { 132 sb.append(','); 133 } 134 sb.append(actual[k]); 135 k++; 136 } 137 if (limit < 0) { 138 sb.append(",..."); 139 } 140 assertEquals(golden, sb.toString()); 141 } 142 143 // Infinite loop, bug 1662110 144 @MediumTest testFrequencyLimits()145 public void testFrequencyLimits() throws Exception { 146 // Rather than checking that we get an exception, 147 // we now want to finish, but in a reasonable time 148 final long tenSeconds = 10000; 149 long start = System.currentTimeMillis(); 150 runRecurrenceIteratorTest( 151 "RRULE:FREQ=SECONDLY;BYSECOND=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14," + 152 "15,16,17,18,19,20,21,22,23,24,25,26,27,28,29," + 153 "30,31,32,33,34,35,36,37,38,39,40,41,42,43,44," + 154 "45,46,47,48,49,50,51,52,53,54,55,56,57,58,59", "20000101", 1, "20000101"); 155 if (System.currentTimeMillis() - start > tenSeconds) { 156 fail("Don't do that"); 157 } 158 } 159 160 @MediumTest testSimpleDaily()161 public void testSimpleDaily() throws Exception { 162 runRecurrenceIteratorTest("RRULE:FREQ=DAILY", "20060120", 5, "20060120,20060121,20060122,20060123,20060124,..."); 163 } 164 165 @MediumTest testSimpleWeekly()166 public void testSimpleWeekly() throws Exception { 167 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY", "20060120", 5, "20060120,20060127,20060203,20060210,20060217,..."); 168 } 169 170 @MediumTest testSimpleMonthly()171 public void testSimpleMonthly() throws Exception { 172 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY", "20060120", 5, "20060120,20060220,20060320,20060420,20060520,..."); 173 } 174 175 @MediumTest testSimpleYearly()176 public void testSimpleYearly() throws Exception { 177 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY", "20060120", 5, "20060120,20070120,20080120,20090120,20100120,...", null, "20120101", UTC); 178 } 179 180 // from section 4.3.10 181 @MediumTest testMultipleByParts()182 public void testMultipleByParts() throws Exception { 183 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU", "19970105", 8, "19970105,19970112,19970119,19970126," + "19990103,19990110,19990117,19990124,..."); 184 } 185 186 @MediumTest testCountWithInterval()187 public void testCountWithInterval() throws Exception { 188 runRecurrenceIteratorTest("RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2", "19970105", 11, "19970105,19970107,19970109,19970111,19970113," + "19970115,19970117,19970119,19970121,19970123"); 189 } 190 191 // from section 4.6.5 192 // Fails: wrong dates 193 @MediumTest 194 @Suppress testNegativeOffsetsA()195 public void testNegativeOffsetsA() throws Exception { 196 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10", "19970105", 5, "19971026,19981025,19991031,20001029,20011028,..."); 197 } 198 199 // Fails: wrong dates 200 @MediumTest 201 @Suppress testNegativeOffsetsB()202 public void testNegativeOffsetsB() throws Exception { 203 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4", "19970105", 5, "19970406,19980405,19990404,20000402,20010401,..."); 204 } 205 206 // Fails: wrong dates 207 @MediumTest 208 @Suppress testNegativeOffsetsC()209 public void testNegativeOffsetsC() throws Exception { 210 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T150000Z", "19970105", 5, "19970406"); 211 } 212 213 // feom section 4.8.5.4 214 @MediumTest testDailyFor10Occ()215 public void testDailyFor10Occ() throws Exception { 216 runRecurrenceIteratorTest("RRULE:FREQ=DAILY;COUNT=10", "19970902T090000", 11, "19970902T090000,19970903T090000,19970904T090000,19970905T090000," + "19970906T090000,19970907T090000,19970908T090000,19970909T090000," + "19970910T090000,19970911T090000"); 217 218 } 219 220 @MediumTest testDailyUntilDec4()221 public void testDailyUntilDec4() throws Exception { 222 runRecurrenceIteratorTest("RRULE:FREQ=DAILY;UNTIL=19971204", "19971128", 11, "19971128,19971129,19971130,19971201,19971202,19971203,19971204"); 223 } 224 225 // Fails: infinite loop 226 @MediumTest 227 @Suppress testEveryOtherDayForever()228 public void testEveryOtherDayForever() throws Exception { 229 runRecurrenceIteratorTest("RRULE:FREQ=DAILY;INTERVAL=2", "19971128", 5, "19971128,19971130,19971202,19971204,19971206,..."); 230 } 231 232 @MediumTest testEvery10Days5Occ()233 public void testEvery10Days5Occ() throws Exception { 234 runRecurrenceIteratorTest("RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5", "19970902", 5, "19970902,19970912,19970922,19971002,19971012"); 235 } 236 237 @MediumTest testWeeklyFor10Occ()238 public void testWeeklyFor10Occ() throws Exception { 239 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;COUNT=10", "19970902", 10, "19970902,19970909,19970916,19970923,19970930," + "19971007,19971014,19971021,19971028,19971104"); 240 } 241 242 @MediumTest testWeeklyUntilDec24()243 public void testWeeklyUntilDec24() throws Exception { 244 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;UNTIL=19971224", "19970902", 25, "19970902,19970909,19970916,19970923,19970930," + "19971007,19971014,19971021,19971028,19971104," + "19971111,19971118,19971125,19971202,19971209," + "19971216,19971223"); 245 } 246 247 @MediumTest testEveryOtherWeekForever()248 public void testEveryOtherWeekForever() throws Exception { 249 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU", "19970902", 11, "19970902,19970916,19970930,19971014,19971028," + "19971111,19971125,19971209,19971223,19980106," + "19980120,..."); 250 } 251 252 @MediumTest testWeeklyOnTuesdayAndThursdayFor5Weeks()253 public void testWeeklyOnTuesdayAndThursdayFor5Weeks() throws Exception { 254 // if UNTIL date does not match start date, then until date treated as 255 // occurring on midnight. 256 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;UNTIL=19971007;WKST=SU;BYDAY=TU,TH", "19970902T090000", 11, "19970902T090000,19970904T090000,19970909T090000,19970911T090000," + "19970916T090000,19970918T090000,19970923T090000,19970925T090000," + "19970930T090000,19971002T090000"); 257 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH", "19970902T090000", 11, "19970902T090000,19970904T090000,19970909T090000,19970911T090000," + "19970916T090000,19970918T090000,19970923T090000,19970925T090000," + "19970930T090000,19971002T090000"); 258 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH", "19970902", 11, "19970902,19970904,19970909,19970911,19970916," + "19970918,19970923,19970925,19970930,19971002"); 259 } 260 261 @MediumTest testEveryOtherWeekOnMWFUntilDec24()262 public void testEveryOtherWeekOnMWFUntilDec24() throws Exception { 263 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;\n" + " BYDAY=MO,WE,FR", "19970903T090000", 25, "19970903T090000,19970905T090000,19970915T090000,19970917T090000," + "19970919T090000,19970929T090000,19971001T090000,19971003T090000," + "19971013T090000,19971015T090000,19971017T090000,19971027T090000," + "19971029T090000,19971031T090000,19971110T090000,19971112T090000," + "19971114T090000,19971124T090000,19971126T090000,19971128T090000," + "19971208T090000,19971210T090000,19971212T090000,19971222T090000"); 264 } 265 266 @MediumTest testEveryOtherWeekOnMWFUntilDec24a()267 public void testEveryOtherWeekOnMWFUntilDec24a() throws Exception { 268 // if the UNTIL date is timed, when the start is not, the time should be 269 // ignored, so we get one more instance 270 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;\n" + " BYDAY=MO,WE,FR", "19970903", 25, "19970903,19970905,19970915,19970917," + "19970919,19970929,19971001,19971003," + "19971013,19971015,19971017,19971027," + "19971029,19971031,19971110,19971112," + "19971114,19971124,19971126,19971128," + "19971208,19971210,19971212,19971222," + "19971224"); 271 } 272 273 // Fails with wrong times 274 @MediumTest 275 @Suppress testEveryOtherWeekOnMWFUntilDec24b()276 public void testEveryOtherWeekOnMWFUntilDec24b() throws Exception { 277 // test with an alternate timezone 278 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T090000Z;WKST=SU;\n" + " BYDAY=MO,WE,FR", "19970903T090000", 25, "19970903T160000,19970905T160000,19970915T160000,19970917T160000," + "19970919T160000,19970929T160000,19971001T160000,19971003T160000," + "19971013T160000,19971015T160000,19971017T160000,19971027T170000," + "19971029T170000,19971031T170000,19971110T170000,19971112T170000," + "19971114T170000,19971124T170000,19971126T170000,19971128T170000," + "19971208T170000,19971210T170000,19971212T170000,19971222T170000", null, PST); 279 } 280 281 @MediumTest testEveryOtherWeekOnTuThFor8Occ()282 public void testEveryOtherWeekOnTuThFor8Occ() throws Exception { 283 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH", "19970902", 8, "19970902,19970904,19970916,19970918,19970930," + "19971002,19971014,19971016"); 284 } 285 286 @MediumTest testMonthlyOnThe1stFridayFor10Occ()287 public void testMonthlyOnThe1stFridayFor10Occ() throws Exception { 288 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR", "19970905", 10, "19970905,19971003,19971107,19971205,19980102," + "19980206,19980306,19980403,19980501,19980605"); 289 } 290 291 @MediumTest testMonthlyOnThe1stFridayUntilDec24()292 public void testMonthlyOnThe1stFridayUntilDec24() throws Exception { 293 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR", "19970905", 4, "19970905,19971003,19971107,19971205"); 294 } 295 296 @MediumTest testEveryOtherMonthOnThe1stAndLastSundayFor10Occ()297 public void testEveryOtherMonthOnThe1stAndLastSundayFor10Occ() throws Exception { 298 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU", "19970907", 10, "19970907,19970928,19971102,19971130,19980104," + "19980125,19980301,19980329,19980503,19980531"); 299 } 300 301 @MediumTest testMonthlyOnTheSecondToLastMondayOfTheMonthFor6Months()302 public void testMonthlyOnTheSecondToLastMondayOfTheMonthFor6Months() throws Exception { 303 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO", "19970922", 6, "19970922,19971020,19971117,19971222,19980119," + "19980216"); 304 } 305 306 @MediumTest testMonthlyOnTheThirdToLastDay()307 public void testMonthlyOnTheThirdToLastDay() throws Exception { 308 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYMONTHDAY=-3", "19970928", 6, "19970928,19971029,19971128,19971229,19980129,19980226,..."); 309 } 310 311 @MediumTest testMonthlyOnThe2ndAnd15thFor10Occ()312 public void testMonthlyOnThe2ndAnd15thFor10Occ() throws Exception { 313 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15", "19970902", 10, "19970902,19970915,19971002,19971015,19971102," + "19971115,19971202,19971215,19980102,19980115"); 314 } 315 316 @MediumTest testMonthlyOnTheFirstAndLastFor10Occ()317 public void testMonthlyOnTheFirstAndLastFor10Occ() throws Exception { 318 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1", "19970930", 10, "19970930,19971001,19971031,19971101,19971130," + "19971201,19971231,19980101,19980131,19980201"); 319 } 320 321 @MediumTest testEvery18MonthsOnThe10thThru15thFor10Occ()322 public void testEvery18MonthsOnThe10thThru15thFor10Occ() throws Exception { 323 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,\n" + " 15", "19970910", 10, "19970910,19970911,19970912,19970913,19970914," + "19970915,19990310,19990311,19990312,19990313"); 324 } 325 326 @MediumTest testEveryTuesdayEveryOtherMonth()327 public void testEveryTuesdayEveryOtherMonth() throws Exception { 328 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU", "19970902", 18, "19970902,19970909,19970916,19970923,19970930," + "19971104,19971111,19971118,19971125,19980106," + "19980113,19980120,19980127,19980303,19980310," + "19980317,19980324,19980331,..."); 329 } 330 331 @MediumTest testYearlyInJuneAndJulyFor10Occurrences()332 public void testYearlyInJuneAndJulyFor10Occurrences() throws Exception { 333 // Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components 334 // are specified, the day is gotten from DTSTART 335 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7", "19970610", 10, "19970610,19970710,19980610,19980710,19990610," + "19990710,20000610,20000710,20010610,20010710"); 336 } 337 338 @MediumTest testEveryOtherYearOnJanuaryFebruaryAndMarchFor10Occurrences()339 public void testEveryOtherYearOnJanuaryFebruaryAndMarchFor10Occurrences() throws Exception { 340 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3", "19970310", 10, "19970310,19990110,19990210,19990310,20010110," + "20010210,20010310,20030110,20030210,20030310"); 341 } 342 343 //Fails: wrong dates 344 @MediumTest 345 @Suppress testEvery3rdYearOnThe1st100thAnd200thDayFor10Occurrences()346 public void testEvery3rdYearOnThe1st100thAnd200thDayFor10Occurrences() throws Exception { 347 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200", "19970101", 10, "19970101,19970410,19970719,20000101,20000409," + "20000718,20030101,20030410,20030719,20060101"); 348 } 349 350 // Fails: infinite loop 351 @MediumTest 352 @Suppress testEvery20thMondayOfTheYearForever()353 public void testEvery20thMondayOfTheYearForever() throws Exception { 354 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=20MO", "19970519", 3, "19970519,19980518,19990517,..."); 355 } 356 357 // Fails: generates wrong dates 358 @MediumTest 359 @Suppress testMondayOfWeekNumber20WhereTheDefaultStartOfTheWeekIsMonday()360 public void testMondayOfWeekNumber20WhereTheDefaultStartOfTheWeekIsMonday() throws Exception { 361 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO", "19970512", 3, "19970512,19980511,19990517,..."); 362 } 363 364 @MediumTest testEveryThursdayInMarchForever()365 public void testEveryThursdayInMarchForever() throws Exception { 366 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH", "19970313", 11, "19970313,19970320,19970327,19980305,19980312," + "19980319,19980326,19990304,19990311,19990318," + "19990325,..."); 367 } 368 369 //Fails: wrong dates 370 @MediumTest 371 @Suppress testEveryThursdayButOnlyDuringJuneJulyAndAugustForever()372 public void testEveryThursdayButOnlyDuringJuneJulyAndAugustForever() throws Exception { 373 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8", "19970605", 39, "19970605,19970612,19970619,19970626,19970703," + "19970710,19970717,19970724,19970731,19970807," + "19970814,19970821,19970828,19980604,19980611," + "19980618,19980625,19980702,19980709,19980716," + "19980723,19980730,19980806,19980813,19980820," + "19980827,19990603,19990610,19990617,19990624," + "19990701,19990708,19990715,19990722,19990729," + "19990805,19990812,19990819,19990826,..."); 374 } 375 376 //Fails: infinite loop 377 @MediumTest 378 @Suppress testEveryFridayThe13thForever()379 public void testEveryFridayThe13thForever() throws Exception { 380 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13", "19970902", 5, "19980213,19980313,19981113,19990813,20001013," + "..."); 381 } 382 383 @MediumTest testTheFirstSaturdayThatFollowsTheFirstSundayOfTheMonthForever()384 public void testTheFirstSaturdayThatFollowsTheFirstSundayOfTheMonthForever() throws Exception { 385 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13", "19970913", 10, "19970913,19971011,19971108,19971213,19980110," + "19980207,19980307,19980411,19980509,19980613," + "..."); 386 } 387 388 @MediumTest testEvery4YearsThe1stTuesAfterAMonInNovForever()389 public void testEvery4YearsThe1stTuesAfterAMonInNovForever() throws Exception { 390 // US Presidential Election Day 391 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,\n" + " 5,6,7,8", "19961105", 3, "19961105,20001107,20041102,..."); 392 } 393 394 // Fails: wrong dates 395 @MediumTest 396 @Suppress testThe3rdInstanceIntoTheMonthOfOneOfTuesWedThursForNext3Months()397 public void testThe3rdInstanceIntoTheMonthOfOneOfTuesWedThursForNext3Months() throws Exception { 398 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3", "19970904", 3, "19970904,19971007,19971106"); 399 } 400 401 // Fails: wrong dates 402 @MediumTest 403 @Suppress testThe2ndToLastWeekdayOfTheMonth()404 public void testThe2ndToLastWeekdayOfTheMonth() throws Exception { 405 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2", "19970929", 7, "19970929,19971030,19971127,19971230,19980129," + "19980226,19980330,..."); 406 } 407 408 // Fails: infinite loop 409 @MediumTest 410 @Suppress testEvery3HoursFrom900AmTo500PmOnASpecificDay()411 public void testEvery3HoursFrom900AmTo500PmOnASpecificDay() throws Exception { 412 runRecurrenceIteratorTest("RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970903T010000Z", "19970902", 7, "00000902,19970909,19970900,19970912,19970900," + "19970915,19970900"); 413 } 414 415 // Fails: infinite loop 416 @MediumTest 417 @Suppress testEvery15MinutesFor6Occurrences()418 public void testEvery15MinutesFor6Occurrences() throws Exception { 419 runRecurrenceIteratorTest("RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6", "19970902", 13, "00000902,19970909,19970900,19970909,19970915," + "19970909,19970930,19970909,19970945,19970910," + "19970900,19970910,19970915"); 420 } 421 422 @MediumTest 423 @Suppress testEveryHourAndAHalfFor4Occurrences()424 public void testEveryHourAndAHalfFor4Occurrences() throws Exception { 425 runRecurrenceIteratorTest("RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4", "19970902", 9, "00000902,19970909,19970900,19970910,19970930," + "19970912,19970900,19970913,19970930"); 426 } 427 428 // Fails: wrong dates 429 @MediumTest 430 @Suppress testAnExampleWhereTheDaysGeneratedMakesADifferenceBecauseOfWkst()431 public void testAnExampleWhereTheDaysGeneratedMakesADifferenceBecauseOfWkst() throws Exception { 432 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO", "19970805", 4, "19970805,19970810,19970819,19970824"); 433 } 434 435 // Fails: wrong dates 436 @MediumTest 437 @Suppress testAnExampleWhereTheDaysGeneratedMakesADifferenceBecauseOfWkst2()438 public void testAnExampleWhereTheDaysGeneratedMakesADifferenceBecauseOfWkst2() throws Exception { 439 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU", "19970805", 8, "19970805,19970817,19970819,19970831"); 440 } 441 442 // Fails: wrong dates 443 @MediumTest 444 @Suppress testWithByDayAndByMonthDayFilter()445 public void testWithByDayAndByMonthDayFilter() throws Exception { 446 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;COUNT=4;BYDAY=TU,SU;" + "BYMONTHDAY=13,14,15,16,17,18,19,20", "19970805", 8, "19970817,19970819,19970914,19970916"); 447 } 448 449 // Failed: wrong dates 450 @MediumTest 451 @Suppress testAnnuallyInAugustOnTuesAndSunBetween13thAnd20th()452 public void testAnnuallyInAugustOnTuesAndSunBetween13thAnd20th() throws Exception { 453 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;COUNT=4;BYDAY=TU,SU;" + "BYMONTHDAY=13,14,15,16,17,18,19,20;BYMONTH=8", "19970605", 8, "19970817,19970819,19980816,19980818"); 454 } 455 456 // Failed: wrong dates 457 @MediumTest 458 @Suppress testLastDayOfTheYearIsASundayOrTuesday()459 public void testLastDayOfTheYearIsASundayOrTuesday() throws Exception { 460 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;COUNT=4;BYDAY=TU,SU;BYYEARDAY=-1", "19940605", 8, "19951231,19961231,20001231,20021231"); 461 } 462 463 // Fails: wrong dates 464 @MediumTest 465 @Suppress testLastWeekdayOfMonth()466 public void testLastWeekdayOfMonth() throws Exception { 467 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYSETPOS=-1;BYDAY=-1MO,-1TU,-1WE,-1TH,-1FR", "19940605", 8, "19940630,19940729,19940831,19940930," + "19941031,19941130,19941230,19950131,..."); 468 } 469 470 // Fails: generates wrong dates 471 @MediumTest 472 @Suppress testMonthsThatStartOrEndOnFriday()473 public void testMonthsThatStartOrEndOnFriday() throws Exception { 474 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYMONTHDAY=1,-1;BYDAY=FR;COUNT=6", "19940605", 8, "19940701,19940930,19950331,19950630,19950901,19951201"); 475 } 476 477 // Fails: can't go that far into future 478 @MediumTest 479 @Suppress testCenturiesThatAreNotLeapYears()480 public void testCenturiesThatAreNotLeapYears() throws Exception { 481 // I can't think of a good reason anyone would want to specify both a 482 // month day and a year day, so here's a really contrived example 483 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=100;BYYEARDAY=60;BYMONTHDAY=1", "19000101", 4, "19000301,21000301,22000301,23000301,...", null, "25000101", UTC); 484 } 485 486 // Fails: generates instances when it shouldn't 487 @MediumTest 488 @Suppress testNoInstancesGenerated()489 public void testNoInstancesGenerated() throws Exception { 490 runRecurrenceIteratorTest("RRULE:FREQ=DAILY;UNTIL=19990101", "20000101", 4, ""); 491 } 492 493 // Fails: generates instances when it shouldn't 494 @MediumTest 495 @Suppress testNoInstancesGenerated2()496 public void testNoInstancesGenerated2() throws Exception { 497 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=30", "20000101", 4, ""); 498 } 499 500 // Fails: generates instances when it shouldn't 501 @MediumTest 502 @Suppress testNoInstancesGenerated3()503 public void testNoInstancesGenerated3() throws Exception { 504 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=4;BYYEARDAY=366", "20000101", 4, ""); 505 } 506 507 //Fails: wrong dates 508 @MediumTest 509 @Suppress testLastWeekdayOfMarch()510 public void testLastWeekdayOfMarch() throws Exception { 511 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYMONTH=3;BYDAY=SA,SU;BYSETPOS=-1", "20000101", 4, "20000326,20010331,20020331,20030330,..."); 512 } 513 514 // Fails: wrong dates 515 @MediumTest 516 @Suppress testFirstWeekdayOfMarch()517 public void testFirstWeekdayOfMarch() throws Exception { 518 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYMONTH=3;BYDAY=SA,SU;BYSETPOS=1", "20000101", 4, "20000304,20010303,20020302,20030301,..."); 519 } 520 521 // January 1999 522 // Mo Tu We Th Fr Sa Su 523 // 1 2 3 // < 4 days, so not a week 524 // 4 5 6 7 8 9 10 525 526 // January 2000 527 // Mo Tu We Th Fr Sa Su 528 // 1 2 // < 4 days, so not a week 529 // 3 4 5 6 7 8 9 530 531 // January 2001 532 // Mo Tu We Th Fr Sa Su 533 // 1 2 3 4 5 6 7 534 // 8 9 10 11 12 13 14 535 536 // January 2002 537 // Mo Tu We Th Fr Sa Su 538 // 1 2 3 4 5 6 539 // 7 8 9 10 11 12 13 540 541 /** 542 * Find the first weekday of the first week of the year. 543 * The first week of the year may be partial, and the first week is considered 544 * to be the first one with at least four days. 545 */ 546 // Fails: wrong dates 547 @MediumTest 548 @Suppress testFirstWeekdayOfFirstWeekOfYear()549 public void testFirstWeekdayOfFirstWeekOfYear() throws Exception { 550 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYWEEKNO=1;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1", "19990101", 4, "19990104,20000103,20010101,20020101,..."); 551 } 552 553 // Fails: wrong dates 554 @MediumTest 555 @Suppress testFirstSundayOfTheYear1()556 public void testFirstSundayOfTheYear1() throws Exception { 557 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYWEEKNO=1;BYDAY=SU", "19990101", 4, "19990110,20000109,20010107,20020106,..."); 558 } 559 560 // Fails: wrong dates 561 @MediumTest 562 @Suppress testFirstSundayOfTheYear2()563 public void testFirstSundayOfTheYear2() throws Exception { 564 // TODO(msamuel): is this right? 565 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=1SU", "19990101", 4, "19990103,20000102,20010107,20020106,..."); 566 } 567 568 // Fails: wrong dates 569 @MediumTest 570 @Suppress testFirstSundayOfTheYear3()571 public void testFirstSundayOfTheYear3() throws Exception { 572 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=SU;BYYEARDAY=1,2,3,4,5,6,7,8,9,10,11,12,13" + ";BYSETPOS=1", "19990101", 4, "19990103,20000102,20010107,20020106,..."); 573 } 574 575 // Fails: wrong dates 576 @MediumTest 577 @Suppress testFirstWeekdayOfYear()578 public void testFirstWeekdayOfYear() throws Exception { 579 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1", "19990101", 4, "19990101,20000103,20010101,20020101,..."); 580 } 581 582 // Fails: wrong dates 583 @MediumTest 584 @Suppress testLastWeekdayOfFirstWeekOfYear()585 public void testLastWeekdayOfFirstWeekOfYear() throws Exception { 586 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYWEEKNO=1;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1", "19990101", 4, "19990108,20000107,20010105,20020104,..."); 587 } 588 589 // January 1999 590 // Mo Tu We Th Fr Sa Su 591 // 1 2 3 592 // 4 5 6 7 8 9 10 593 // 11 12 13 14 15 16 17 594 // 18 19 20 21 22 23 24 595 // 25 26 27 28 29 30 31 596 597 // Fails: wrong dates 598 @MediumTest 599 @Suppress testSecondWeekday1()600 public void testSecondWeekday1() throws Exception { 601 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=2", "19990101", 4, "19990105,19990112,19990119,19990126,..."); 602 } 603 604 // January 1997 605 // Mo Tu We Th Fr Sa Su 606 // 1 2 3 4 5 607 // 6 7 8 9 10 11 12 608 // 13 14 15 16 17 18 19 609 // 20 21 22 23 24 25 26 610 // 27 28 29 30 31 611 612 // Fails: wrong dates 613 @MediumTest 614 @Suppress testSecondWeekday2()615 public void testSecondWeekday2() throws Exception { 616 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=2", "19970101", 4, "19970102,19970107,19970114,19970121,..."); 617 } 618 619 // Fails: wrong dates 620 @MediumTest 621 @Suppress testByYearDayAndByDayFilterInteraction()622 public void testByYearDayAndByDayFilterInteraction() throws Exception { 623 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYYEARDAY=15;BYDAY=3MO", "19990101", 4, "20010115,20070115,20180115,20240115,..."); 624 } 625 626 // Fails: wrong dates 627 @MediumTest 628 @Suppress testByDayWithNegWeekNoAsFilter()629 public void testByDayWithNegWeekNoAsFilter() throws Exception { 630 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;BYMONTHDAY=26;BYDAY=-1FR", "19990101", 4, "19990226,19990326,19991126,20000526,..."); 631 } 632 633 // Fails: wrong dates 634 @MediumTest 635 @Suppress testLastWeekOfTheYear()636 public void testLastWeekOfTheYear() throws Exception { 637 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYWEEKNO=-1", "19990101", 6, "19991227,19991228,19991229,19991230,19991231,20001225,..."); 638 } 639 640 // Fails: not enough dates generated 641 @MediumTest 642 @Suppress testUserSubmittedTest1()643 public void testUserSubmittedTest1() throws Exception { 644 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=WE;BYDAY=SU,TU,TH,SA" + ";UNTIL=20000215T113000Z", "20000127T033000", 20, "20000127T033000,20000129T033000,20000130T033000,20000201T033000," + "20000210T033000,20000212T033000,20000213T033000,20000215T033000"); 645 } 646 647 @MediumTest testAdvanceTo1()648 public void testAdvanceTo1() throws Exception { 649 // a bunch of tests grabbed from above with an advance-to date tacked on 650 651 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH", "19970313", 11, 652 /*"19970313,19970320,19970327,"*/"19980305,19980312," + "19980319,19980326,19990304,19990311,19990318," + "19990325,20000302,20000309,20000316,...", "19970601", UTC); 653 } 654 655 // Fails: infinite loop 656 @MediumTest 657 @Suppress testAdvanceTo2()658 public void testAdvanceTo2() throws Exception { 659 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYDAY=20MO", "19970519", 3, 660 /*"19970519,"*/"19980518,19990517,20000515,...", "19980515", UTC); 661 } 662 663 // Fails: wrong dates 664 @MediumTest 665 @Suppress testAdvanceTo3()666 public void testAdvanceTo3() throws Exception { 667 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=3;UNTIL=20090101;BYYEARDAY=1,100,200", "19970101", 10, 668 /*"19970101,19970410,19970719,20000101,"*/"20000409," + "20000718,20030101,20030410,20030719,20060101,20060410,20060719," + "20090101", "20000228", UTC); 669 } 670 671 //Fails: wrong dates 672 @MediumTest 673 @Suppress testAdvanceTo4()674 public void testAdvanceTo4() throws Exception { 675 // make sure that count preserved 676 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200", "19970101", 10, 677 /*"19970101,19970410,19970719,20000101,"*/"20000409," + "20000718,20030101,20030410,20030719,20060101", "20000228", UTC); 678 } 679 680 // Fails: too many dates 681 @MediumTest 682 @Suppress testAdvanceTo5()683 public void testAdvanceTo5() throws Exception { 684 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3", "19970310", 10, 685 /*"19970310,"*/"19990110,19990210,19990310,20010110," + "20010210,20010310,20030110,20030210,20030310", "19980401", UTC); 686 } 687 688 @MediumTest testAdvanceTo6()689 public void testAdvanceTo6() throws Exception { 690 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;UNTIL=19971224", "19970902", 25, 691 /*"19970902,19970909,19970916,19970923,"*/"19970930," + "19971007,19971014,19971021,19971028,19971104," + "19971111,19971118,19971125,19971202,19971209," + "19971216,19971223", "19970930", UTC); 692 } 693 694 @MediumTest testAdvanceTo7()695 public void testAdvanceTo7() throws Exception { 696 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;INTERVAL=18;BYMONTHDAY=10,11,12,13,14,\n" + " 15", "19970910", 5, 697 /*"19970910,19970911,19970912,19970913,19970914," + 698 "19970915,"*/"19990310,19990311,19990312,19990313,19990314,...", "19990101", UTC); 699 } 700 701 @MediumTest testAdvanceTo8()702 public void testAdvanceTo8() throws Exception { 703 // advancing into the past 704 runRecurrenceIteratorTest("RRULE:FREQ=MONTHLY;INTERVAL=18;BYMONTHDAY=10,11,12,13,14,\n" + " 15", "19970910", 11, "19970910,19970911,19970912,19970913,19970914," + "19970915,19990310,19990311,19990312,19990313,19990314,...", "19970901", UTC); 705 } 706 707 // Fails: wrong dates 708 @MediumTest 709 @Suppress testAdvanceTo9()710 public void testAdvanceTo9() throws Exception { 711 // skips first instance 712 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=100;BYMONTH=2;BYMONTHDAY=29", "19000101", 5, 713 // would return 2000 714 "24000229,28000229,32000229,36000229,40000229,...", "20040101", UTC); 715 } 716 717 // Infinite loop in native code (bug 1686327) 718 @MediumTest 719 @Suppress testAdvanceTo10()720 public void testAdvanceTo10() throws Exception { 721 // filter hits until date before first instnace 722 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;INTERVAL=100;BYMONTH=2;BYMONTHDAY=29;UNTIL=21000101", "19000101", 5, "", "20040101", UTC); 723 } 724 725 // Fails: generates wrong dates 726 @MediumTest 727 @Suppress testAdvanceTo11()728 public void testAdvanceTo11() throws Exception { 729 // advancing something that returns no instances 730 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=30", "20000101", 10, "", "19970901", UTC); 731 } 732 733 // Fails: generates wrong dates 734 @MediumTest 735 @Suppress testAdvanceTo12()736 public void testAdvanceTo12() throws Exception { 737 // advancing something that returns no instances and has a BYSETPOS rule 738 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=30,31;BYSETPOS=1", "20000101", 10, "", "19970901", UTC); 739 } 740 741 // Fails: generates wrong dates 742 @MediumTest 743 @Suppress testAdvanceTo13()744 public void testAdvanceTo13() throws Exception { 745 // advancing way past year generator timeout 746 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=28", "20000101", 10, "", "25000101", UTC); 747 } 748 749 /** 750 * a testcase that yielded dupes due to bysetPos evilness 751 */ 752 @MediumTest 753 @Suppress testCaseThatYieldedDupes()754 public void testCaseThatYieldedDupes() throws Exception { 755 runRecurrenceIteratorTest("RRULE:FREQ=WEEKLY;WKST=SU;INTERVAL=1;BYMONTH=9,1,12,8" + ";BYMONTHDAY=-9,-29,24;BYSETPOS=-1,-4,10,-6,-1,-10,-10,-9,-8", "20060528", 200, "20060924,20061203,20061224,20070902,20071223,20080803,20080824," + "20090823,20100103,20100124,20110123,20120902,20121223,20130922," + "20140803,20140824,20150823,20160103,20160124,20170924,20171203," + "20171224,20180902,20181223,20190922,20200823,20210103,20210124," + "20220123,20230924,20231203,20231224,20240922,20250803,20250824," + "20260823,20270103,20270124,20280123,20280924,20281203,20281224," + "20290902,20291223,20300922,20310803,20310824,20330123,20340924," + "20341203,20341224,20350902,20351223,20360803,20360824,20370823," + "20380103,20380124,20390123,20400902,20401223,20410922,20420803," + "20420824,20430823,20440103,20440124,20450924,20451203,20451224," + "20460902,20461223,20470922,20480823,20490103,20490124,20500123," + "20510924,20511203,20511224,20520922,20530803,20530824,20540823," + "20550103,20550124,20560123,20560924,20561203,20561224,20570902," + "20571223,20580922,20590803,20590824,20610123,20620924,20621203," + "20621224,20630902,20631223,20640803,20640824,20650823,20660103," + "20660124,20670123,20680902,20681223,20690922,20700803,20700824," + "20710823,20720103,20720124,20730924,20731203,20731224,20740902," + "20741223,20750922,20760823,20770103,20770124,20780123,20790924," + "20791203,20791224,20800922,20810803,20810824,20820823,20830103," + "20830124,20840123,20840924,20841203,20841224,20850902,20851223," + "20860922,20870803,20870824,20890123,20900924,20901203,20901224," + "20910902,20911223,20920803,20920824,20930823,20940103,20940124," + "20950123,20960902,20961223,20970922,20980803,20980824,20990823," + "21000103,21000124,21010123,21020924,21021203,21021224,21030902," + "21031223,21040803,21040824,21050823,21060103,21060124,21070123," + "21080902,21081223,21090922,21100803,21100824,21110823,21120103," + "21120124,21130924,21131203,21131224,21140902,21141223,21150922," + "21160823,21170103,21170124,21180123,21190924,21191203,21191224," + "21200922,21210803,21210824,21220823,..."); 756 } 757 758 @MediumTest testEveryThursdayinMarchEachYear()759 public void testEveryThursdayinMarchEachYear() throws Exception { 760 runRecurrenceIteratorTest("RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH", "20100304", 9, "20100304,20100311,20100318,20100325,20110303,20110310,20110317,20110324,20110331", null, "20111231", UTC); 761 } 762 } 763