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