1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.calendarcommon2;
18 
19 import com.android.calendarcommon2.ICalendar;
20 import com.android.calendarcommon2.RecurrenceSet;
21 
22 import android.content.ContentValues;
23 import android.test.suitebuilder.annotation.SmallTest;
24 import android.util.Log;
25 import android.provider.CalendarContract;
26 import junit.framework.TestCase;
27 
28 import java.util.Arrays;
29 import java.util.List;
30 
31 /**
32  * Test some pim.RecurrenceSet functionality.
33  */
34 public class RecurrenceSetTest extends TestCase {
35 
36     // Test a recurrence
37     @SmallTest
testRecurrenceSet0()38     public void testRecurrenceSet0() throws Exception {
39         String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n"
40                 + "DTEND;TZID=America/New_York:20080221T190000\n"
41                 + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n"
42                 + "EXDATE:20080222T120000Z";
43         final ContentValues values = verifyPopulateContentValues(recurrence,
44                 "FREQ=DAILY;UNTIL=20080222T000000Z", null,
45                 null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0, false);
46         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
47                 new String[] {"FREQ=DAILY;UNTIL=20080222T000000Z"}, null,
48                 null, new Long[] {1203681600000L});
49     }
50 
51     // Test 1 day all-day event
52     @SmallTest
testRecurrenceSet1()53     public void testRecurrenceSet1() throws Exception {
54         String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n"
55                 + "RRULE:FREQ=YEARLY;WKST=SU";
56         final ContentValues values = verifyPopulateContentValues(recurrence,
57                 "FREQ=YEARLY;WKST=SU", null, null, null, 1250812800000L, "UTC", "P1D", 1, false);
58         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
59                 new String[] {"FREQ=YEARLY;WKST=SU"}, null, null, null);
60     }
61 
62     // Test 2 day all-day event
63     @SmallTest
testRecurrenceSet2()64     public void testRecurrenceSet2() throws Exception {
65         String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n"
66                 + "RRULE:FREQ=YEARLY;WKST=SU";
67         final ContentValues values = verifyPopulateContentValues(recurrence,
68                 "FREQ=YEARLY;WKST=SU", null, null, null, 1250812800000L, "UTC",  "P2D", 1, false);
69         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
70                 new String[] {"FREQ=YEARLY;WKST=SU"}, null, null, null);
71     }
72 
73     // Test multi-rule RRULE.
74     @SmallTest
testRecurrenceSet3()75     public void testRecurrenceSet3() throws Exception {
76         String recurrence = "DTSTART;VALUE=DATE:20090821\n"
77                 + "RRULE:FREQ=YEARLY;WKST=SU\n"
78                 + "RRULE:FREQ=MONTHLY;COUNT=3\n"
79                 + "DURATION:P2H";
80         final ContentValues values = verifyPopulateContentValues(recurrence,
81                 "FREQ=YEARLY;WKST=SU\nFREQ=MONTHLY;COUNT=3", null,
82                 null, null, 1250812800000L, "UTC", "P2H", 1 /*allDay*/, false);
83         // allDay=1 just means the start time is 00:00:00 UTC.
84         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
85                 new String[] {"FREQ=YEARLY;WKST=SU", "FREQ=MONTHLY;COUNT=3"},
86                 null, null, null);
87     }
88 
89     // Test RDATE with VALUE=DATE.
90     @SmallTest
testRecurrenceSet4()91     public void testRecurrenceSet4() throws Exception {
92         String recurrence = "DTSTART;TZID=America/Los_Angeles:20090821T010203\n"
93                 + "RDATE;TZID=America/Los_Angeles;VALUE=DATE:20110601,20110602,20110603\n"
94                 + "DURATION:P2H";
95         final ContentValues values = verifyPopulateContentValues(recurrence, null,
96                 //"TZID=America/Los_Angeles;VALUE=DATE:20110601,20110602,20110603",
97                 "America/Los_Angeles;20110601,20110602,20110603", // incorrect
98                 null, null, 1250841723000L, "America/Los_Angeles", "P2H", 0 /*allDay*/, false);
99         // allDay=1 just means the start time is 00:00:00 UTC.
100         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
101                 null, new Long[] {1306911600000L, 1306998000000L, 1307084400000L}, null, null);
102     }
103 
104     // Check generation of duration from events in different time zones.
105     @SmallTest
testRecurrenceSet5()106     public void testRecurrenceSet5() throws Exception {
107         String recurrence = "DTSTART;TZID=America/Los_Angeles:20090821T070000\n"
108                 + "DTEND;TZID=America/New_York:20090821T110000\n"
109                 + "RRULE:FREQ=YEARLY\n";
110         final ContentValues values = verifyPopulateContentValues(recurrence, "FREQ=YEARLY", null,
111                 null, null, 1250863200000L, "America/Los_Angeles", "P3600S" /*P1H*/, 0 /*allDay*/,
112                 false);
113         // TODO: would like to use P1H for duration
114         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
115                 new String[] {"FREQ=YEARLY"}, null, null, null);
116 
117         String recurrence2 = "DTSTART;TZID=America/New_York:20090821T100000\n"
118             + "DTEND;TZID=America/Los_Angeles:20090821T080000\n"
119             + "RRULE:FREQ=YEARLY\n";
120         final ContentValues values2 = verifyPopulateContentValues(recurrence2, "FREQ=YEARLY", null,
121                 null, null, 1250863200000L, "America/New_York", "P3600S" /*P1H*/, 0 /*allDay*/,
122                 false);
123         // TODO: should we rigorously define which tzid becomes the "event timezone"?
124         verifyRecurrenceSetInitialization(new RecurrenceSet(values2),
125                 new String[] {"FREQ=YEARLY"}, null, null, null);
126     }
127 
128     // Test multi-rule EXRULE.
129     @SmallTest
testRecurrenceSet6()130     public void testRecurrenceSet6() throws Exception {
131         final String recurrence = "DTSTART;VALUE=DATE:20090821\n"
132                 + "RRULE:FREQ=YEARLY;WKST=SU\n"
133                 + "RRULE:FREQ=MONTHLY;COUNT=6\n"
134                 + "EXRULE:FREQ=YEARLY;INTERVAL=4\n"
135                 + "EXRULE:FREQ=MONTHLY;INTERVAL=2\n"
136                 + "EXDATE:20120821\n"
137                 + "DURATION:P2H";
138         final ContentValues values = verifyPopulateContentValues(recurrence,
139                 "FREQ=YEARLY;WKST=SU\nFREQ=MONTHLY;COUNT=6", null,
140                 "FREQ=YEARLY;INTERVAL=4\nFREQ=MONTHLY;INTERVAL=2",
141                 "20120821", 1250812800000L, "UTC", "P2H", 1, false);
142         verifyRecurrenceSetInitialization(new RecurrenceSet(values),
143                 new String[] {"FREQ=YEARLY;WKST=SU", "FREQ=MONTHLY;COUNT=6"}, null,
144                 new String[] {"FREQ=YEARLY;INTERVAL=4", "FREQ=MONTHLY;INTERVAL=2"},
145                 new Long[] {1345507200000L});
146     }
147 
148     // Test multi-rule RDATE and EXDATE.
149     @SmallTest
testRecurrentSet7()150     public void testRecurrentSet7() throws Exception {
151         final RecurrenceSet rs = new RecurrenceSet(
152                 "FREQ=YEARLY;WKST=SU",
153                 "America/Los_Angeles;20110601,20110602\n20110603T120000Z",
154                 "FREQ=YEARLY;INTERVAL=4",
155                 "America/New_York;20120601,20120602\n20120603T120000Z");
156         verifyRecurrenceSetInitialization(rs,
157                 new String[] {"FREQ=YEARLY;WKST=SU"},
158                 new Long[] {1306911600000L, 1306998000000L, 1307102400000L},
159                 new String[] {"FREQ=YEARLY;INTERVAL=4"},
160                 new Long[] {1338523200000L, 1338609600000L, 1338724800000L});
161     }
162 
163     // Test a failure to parse the recurrence data
164     @SmallTest
testRecurrenceSetBadDstart()165     public void testRecurrenceSetBadDstart() throws Exception {
166         String recurrence = "DTSTART;TZID=GMT+05:30:20080221T070000\n"
167                 + "DTEND;TZID=GMT+05:30:20080221T190000\n"
168                 + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n"
169                 + "EXDATE:20080222T120000Z";
170         verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null,
171                 null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0, true);
172     }
173 
174     @SmallTest
testRecurrenceSetBadRrule()175     public void testRecurrenceSetBadRrule() throws Exception {
176         String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n"
177                 + "DTEND;TZID=GMT+05:30:20080221T190000\n"
178                 + "RRULE:FREQ=NEVER;UNTIL=20080222T000000Z\n"
179                 + "EXDATE:20080222T120000Z";
180         verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null,
181                 null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0, true);
182     }
183 
verifyRecurrenceSetInitialization(RecurrenceSet rs, String[] expectedRruleStrs, Long[] expectedRdates, String[] expectedExruleStrs, Long[] expectedExdates)184     private void verifyRecurrenceSetInitialization(RecurrenceSet rs,
185             String[] expectedRruleStrs, Long[] expectedRdates,
186             String[] expectedExruleStrs, Long[] expectedExdates) {
187         verify(convertToEventRecurrences(expectedRruleStrs), rs.rrules);
188         verify(expectedRdates, convertToLong(rs.rdates));
189         verify(convertToEventRecurrences(expectedExruleStrs), rs.exrules);
190         verify(expectedExdates, convertToLong(rs.exdates));
191     }
192 
convertToEventRecurrences(String[] ruleStrs)193     private EventRecurrence[] convertToEventRecurrences(String[] ruleStrs) {
194         if (ruleStrs == null) {
195             return null;
196         }
197         final EventRecurrence[] rules = new EventRecurrence[ruleStrs.length];
198         for (int i = 0; i < ruleStrs.length; ++i) {
199             rules[i] = new EventRecurrence();
200             rules[i].parse(ruleStrs[i]);
201         }
202         return rules;
203     }
204 
convertToLong(long[] primitives)205     private Long[] convertToLong(long[] primitives) {
206         if (primitives == null) {
207             return null;
208         }
209         final Long[] datesLong = new Long[primitives.length];
210         for (int i = 0; i < primitives.length; ++i) {
211             datesLong[i] = primitives[i];
212         }
213         return datesLong;
214     }
215 
verify(Object[] expected, Object[] actual)216     private void verify(Object[] expected, Object[] actual) {
217         if (actual == null && expected == null) {
218             return;
219         }
220         assertNotNull("actual result is null but expected is not. Expected: "
221                 + Arrays.toString(expected), actual);
222         assertNotNull("expected result is null but actual is not. Actual: "
223                 + Arrays.toString(actual), expected);
224         assertEquals("Expected and actual are not of same size."
225                 + "Expected: " + Arrays.toString(expected) + " Actual: " + Arrays.toString(actual),
226                         expected.length, actual.length);
227         List<Object> actualList = Arrays.asList(actual);
228         for (int i = 0; i < expected.length; ++i) {
229             if (!actualList.contains(expected[i])) {
230                 fail("Expected: " + expected[i] + " but not found in Actual: "
231                         + Arrays.toString(actual));
232             }
233         }
234     }
235 
236     // run populateContentValues and verify the results
verifyPopulateContentValues(String recurrence, String rrule, String rdate, String exrule, String exdate, long dtstart, String tzid, String duration, int allDay, boolean badFormat)237     private ContentValues verifyPopulateContentValues(String recurrence, String rrule, String rdate,
238             String exrule, String exdate, long dtstart, String tzid, String duration, int allDay,
239             boolean badFormat)
240             throws ICalendar.FormatException {
241         ICalendar.Component recurrenceComponent =
242                 new ICalendar.Component("DUMMY", null /* parent */);
243         ICalendar.parseComponent(recurrenceComponent, recurrence);
244         ContentValues values = new ContentValues();
245         boolean result = RecurrenceSet.populateContentValues(recurrenceComponent, values);
246         Log.d("KS", "values " + values);
247 
248         if (badFormat) {
249             assertEquals(result, !badFormat);
250             return null;
251         }
252         assertEquals(rrule, values.get(android.provider.CalendarContract.Events.RRULE));
253         assertEquals(rdate, values.get(android.provider.CalendarContract.Events.RDATE));
254         assertEquals(exrule, values.get(android.provider.CalendarContract.Events.EXRULE));
255         assertEquals(exdate, values.get(android.provider.CalendarContract.Events.EXDATE));
256         assertEquals(dtstart, (long) values.getAsLong(CalendarContract.Events.DTSTART));
257         assertEquals(tzid, values.get(android.provider.CalendarContract.Events.EVENT_TIMEZONE));
258         assertEquals(duration, values.get(android.provider.CalendarContract.Events.DURATION));
259         assertEquals(allDay,
260                 (int) values.getAsInteger(android.provider.CalendarContract.Events.ALL_DAY));
261         return values;
262     }
263 
264 }
265