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