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