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