15862a85e17e81866ca82a9905577931947fbd44eMarc Blank/*
25862a85e17e81866ca82a9905577931947fbd44eMarc Blank * Copyright (C) 2010 The Android Open Source Project
35862a85e17e81866ca82a9905577931947fbd44eMarc Blank *
45862a85e17e81866ca82a9905577931947fbd44eMarc Blank * Licensed under the Apache License, Version 2.0 (the "License");
55862a85e17e81866ca82a9905577931947fbd44eMarc Blank * you may not use this file except in compliance with the License.
65862a85e17e81866ca82a9905577931947fbd44eMarc Blank * You may obtain a copy of the License at
75862a85e17e81866ca82a9905577931947fbd44eMarc Blank *
85862a85e17e81866ca82a9905577931947fbd44eMarc Blank *      http://www.apache.org/licenses/LICENSE-2.0
95862a85e17e81866ca82a9905577931947fbd44eMarc Blank *
105862a85e17e81866ca82a9905577931947fbd44eMarc Blank * Unless required by applicable law or agreed to in writing, software
115862a85e17e81866ca82a9905577931947fbd44eMarc Blank * distributed under the License is distributed on an "AS IS" BASIS,
125862a85e17e81866ca82a9905577931947fbd44eMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135862a85e17e81866ca82a9905577931947fbd44eMarc Blank * See the License for the specific language governing permissions and
145862a85e17e81866ca82a9905577931947fbd44eMarc Blank * limitations under the License.
155862a85e17e81866ca82a9905577931947fbd44eMarc Blank */
165862a85e17e81866ca82a9905577931947fbd44eMarc Blank
175862a85e17e81866ca82a9905577931947fbd44eMarc Blankpackage com.android.exchange.utility;
185862a85e17e81866ca82a9905577931947fbd44eMarc Blank
193f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.content.ContentValues;
203f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.content.Entity;
213f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.content.res.Resources;
223f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.provider.CalendarContract.Attendees;
233f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.provider.CalendarContract.Events;
243f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.test.suitebuilder.annotation.MediumTest;
253f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blankimport android.util.Log;
263f8e329b8d638d02e0b48ad504b18690cefc68e9Marc Blank
27c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Address;
287372782488977df778a33d990401ce9e397f646bMarc Blankimport com.android.emailcommon.provider.Account;
29c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment;
30c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Message;
31c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.utility.Utility;
32c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.exchange.R;
33f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankimport com.android.exchange.adapter.CalendarSyncAdapter;
34c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.exchange.adapter.CalendarSyncAdapter.EasCalendarSyncParser;
35f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankimport com.android.exchange.adapter.Parser;
36f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankimport com.android.exchange.adapter.Serializer;
37f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankimport com.android.exchange.adapter.SyncAdapterTestCase;
38f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankimport com.android.exchange.adapter.Tags;
395c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
401c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blankimport java.io.BufferedReader;
41f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankimport java.io.ByteArrayInputStream;
421c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blankimport java.io.IOException;
431c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blankimport java.io.StringReader;
449ca8918b82221dd441293973ffb84d565a52993aMarc Blankimport java.text.DateFormat;
451c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blankimport java.util.ArrayList;
46c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport java.util.Calendar;
47820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blankimport java.util.Date;
48c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport java.util.GregorianCalendar;
491c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blankimport java.util.HashMap;
505862a85e17e81866ca82a9905577931947fbd44eMarc Blankimport java.util.TimeZone;
515862a85e17e81866ca82a9905577931947fbd44eMarc Blank
52377230593dca7cb01483bfaf93959e5821f5f028Marc Blank/**
53377230593dca7cb01483bfaf93959e5821f5f028Marc Blank * Tests of EAS Calendar Utilities
54377230593dca7cb01483bfaf93959e5821f5f028Marc Blank * You can run this entire test case with:
55c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank *   runtest -c com.android.exchange.utility.CalendarUtilitiesTests exchange
56377230593dca7cb01483bfaf93959e5821f5f028Marc Blank *
57377230593dca7cb01483bfaf93959e5821f5f028Marc Blank * Please see RFC2445 for RRULE definition
58377230593dca7cb01483bfaf93959e5821f5f028Marc Blank * http://www.ietf.org/rfc/rfc2445.txt
59377230593dca7cb01483bfaf93959e5821f5f028Marc Blank */
60604a1cf6f2401d78f57edcfb702d560d9c80dd14Marc Blank@MediumTest
61f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blankpublic class CalendarUtilitiesTests extends SyncAdapterTestCase<CalendarSyncAdapter> {
625862a85e17e81866ca82a9905577931947fbd44eMarc Blank
635862a85e17e81866ca82a9905577931947fbd44eMarc Blank    // Some prebuilt time zones, Base64 encoded (as they arrive from EAS)
64377230593dca7cb01483bfaf93959e5821f5f028Marc Blank    // More time zones to be added over time
65377230593dca7cb01483bfaf93959e5821f5f028Marc Blank
66377230593dca7cb01483bfaf93959e5821f5f028Marc Blank    // Not all time zones are appropriate for testing.  For example, ISRAEL_STANDARD_TIME cannot be
67377230593dca7cb01483bfaf93959e5821f5f028Marc Blank    // used because DST is determined from year to year in a non-standard way (related to the lunar
68377230593dca7cb01483bfaf93959e5821f5f028Marc Blank    // calendar); therefore, the test would only work during the year in which it was created
6979268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank
7079268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank    // This time zone has no DST
7179268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank    private static final String ASIA_CALCUTTA_TIME =
725862a85e17e81866ca82a9905577931947fbd44eMarc Blank        "tv7//0kAbgBkAGkAYQAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
735862a85e17e81866ca82a9905577931947fbd44eMarc Blank        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEkAbgBkAGkAYQAgAEQAYQB5AGwAaQBnAGgAdAAgAFQAaQBtAGUA" +
745862a85e17e81866ca82a9905577931947fbd44eMarc Blank        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
7579268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank
7679268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank    // This time zone is equivalent to PST and uses DST
7779268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank    private static final String AMERICA_DAWSON_TIME =
785862a85e17e81866ca82a9905577931947fbd44eMarc Blank        "4AEAAFAAYQBjAGkAZgBpAGMAIABTAHQAYQBuAGQAYQByAGQAIABUAGkAbQBlAAAAAAAAAAAAAAAAAAAAAAAA" +
795862a85e17e81866ca82a9905577931947fbd44eMarc Blank        "AAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAFAAYQBjAGkAZgBpAGMAIABEAGEAeQBsAGkAZwBoAHQAIABUAGkA" +
805862a85e17e81866ca82a9905577931947fbd44eMarc Blank        "bQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAIAAAAAAAAAxP///w==";
815862a85e17e81866ca82a9905577931947fbd44eMarc Blank
8279268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank    // Test a southern hemisphere time zone w/ DST
8379268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank    private static final String AUSTRALIA_ACT_TIME =
8479268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        "qP3//0EAVQBTACAARQBhAHMAdABlAHIAbgAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAA" +
8579268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        "AAAAAAAAAAQAAAABAAMAAAAAAAAAAAAAAEEAVQBTACAARQBhAHMAdABlAHIAbgAgAEQAYQB5AGwAaQBnAGgA" +
8679268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        "dAAgAFQAaQBtAGUAAAAAAAAAAAAAAAAAAAAAAAoAAAABAAIAAAAAAAAAxP///w==";
8779268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank
88270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    // Test a timezone with GMT bias but bogus DST parameters (there is no equivalent time zone
89270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    // in the database)
90270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    private static final String GMT_UNKNOWN_DAYLIGHT_TIME =
91270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        "AAAAACgARwBNAFQAKwAwADAAOgAwADAAKQAgAFQAaQBtAGUAIABaAG8AbgBlAAAAAAAAAAAAAAAAAAAAAAAA" +
92270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        "AAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAACgARwBNAFQAKwAwADAAOgAwADAAKQAgAFQAaQBtAGUAIABaAG8A" +
93270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        "bgBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAEAAAAAAAAAxP///w==";
94270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
955bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank    // This time zone has no DST, but earlier, buggy code retrieved a TZ WITH DST
965bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank    private static final String ARIZONA_TIME =
975bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank        "pAEAAFUAUwAgAE0AbwB1AG4AdABhAGkAbgAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAA" +
985bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUAUwAgAE0AbwB1AG4AdABhAGkAbgAgAEQAYQB5AGwAaQBnAGgA" +
995bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank        "dAAgAFQAaQBtAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
1005bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank
1014868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank    private static final String HAWAII_TIME =
1024868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        "WAIAAEgAYQB3AGEAaQBpAGEAbgAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAAAAAAAAAA" +
1034868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgAYQB3AGEAaQBpAGEAbgAgAEQAYQB5AGwAaQBnAGgAdAAgAFQA" +
1044868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        "aQBtAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
1054868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank
1062c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank    // This is time zone sent by Exchange 2007, apparently; the start time of DST for the eastern
1072c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank    // time zone (EST) is off by two hours, which we should correct in our new "lenient" code
1082c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank    private static final String LENIENT_EASTERN_TIME =
1092c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        "LAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
1102c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        "AAAAAAAAAAsAAAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
1112c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAACAAAAAAAAAAAAxP///w==";
1122c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank
1137b873db28bda10f98f3782b7b161cb66117034d7Marc Blank    // This string specifies "Europe/London" in the name, but otherwise is somewhat bogus
1147b873db28bda10f98f3782b7b161cb66117034d7Marc Blank    // in that it has unknown time zone dates with a 0 bias (GMT). (From a Zimbra server user)
1157b873db28bda10f98f3782b7b161cb66117034d7Marc Blank    private static final String EUROPE_LONDON_TIME_BY_NAME =
1167b873db28bda10f98f3782b7b161cb66117034d7Marc Blank        "AAAAAEV1cm9wZS9Mb25kb24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
1177b873db28bda10f98f3782b7b161cb66117034d7Marc Blank        "AAAAAAAAAAoAAQAFAAIAAAAAAAAAAAAAAEV1cm9wZS9Mb25kb24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
1187b873db28bda10f98f3782b7b161cb66117034d7Marc Blank        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAFAAEAAAAAAAAAxP///w==";
1197b873db28bda10f98f3782b7b161cb66117034d7Marc Blank
1209ca8918b82221dd441293973ffb84d565a52993aMarc Blank    private static final String ORGANIZER = "organizer@server.com";
1219ca8918b82221dd441293973ffb84d565a52993aMarc Blank    private static final String ATTENDEE = "attendee@server.com";
1229ca8918b82221dd441293973ffb84d565a52993aMarc Blank
1235862a85e17e81866ca82a9905577931947fbd44eMarc Blank    public void testGetSet() {
1245862a85e17e81866ca82a9905577931947fbd44eMarc Blank        byte[] bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7};
1255862a85e17e81866ca82a9905577931947fbd44eMarc Blank
1265862a85e17e81866ca82a9905577931947fbd44eMarc Blank        // First, check that getWord/Long are properly little endian
1275862a85e17e81866ca82a9905577931947fbd44eMarc Blank        assertEquals(0x0100, CalendarUtilities.getWord(bytes, 0));
1285862a85e17e81866ca82a9905577931947fbd44eMarc Blank        assertEquals(0x03020100, CalendarUtilities.getLong(bytes, 0));
1295862a85e17e81866ca82a9905577931947fbd44eMarc Blank        assertEquals(0x07060504, CalendarUtilities.getLong(bytes, 4));
1305862a85e17e81866ca82a9905577931947fbd44eMarc Blank
1315862a85e17e81866ca82a9905577931947fbd44eMarc Blank        // Set some words and longs
1325862a85e17e81866ca82a9905577931947fbd44eMarc Blank        CalendarUtilities.setWord(bytes, 0, 0xDEAD);
1335862a85e17e81866ca82a9905577931947fbd44eMarc Blank        CalendarUtilities.setLong(bytes, 2, 0xBEEFBEEF);
1345862a85e17e81866ca82a9905577931947fbd44eMarc Blank        CalendarUtilities.setWord(bytes, 6, 0xCEDE);
1355862a85e17e81866ca82a9905577931947fbd44eMarc Blank
1365862a85e17e81866ca82a9905577931947fbd44eMarc Blank        // Retrieve them
1375862a85e17e81866ca82a9905577931947fbd44eMarc Blank        assertEquals(0xDEAD, CalendarUtilities.getWord(bytes, 0));
1385862a85e17e81866ca82a9905577931947fbd44eMarc Blank        assertEquals(0xBEEFBEEF, CalendarUtilities.getLong(bytes, 2));
1395862a85e17e81866ca82a9905577931947fbd44eMarc Blank        assertEquals(0xCEDE, CalendarUtilities.getWord(bytes, 6));
1405862a85e17e81866ca82a9905577931947fbd44eMarc Blank    }
1415862a85e17e81866ca82a9905577931947fbd44eMarc Blank
1425862a85e17e81866ca82a9905577931947fbd44eMarc Blank    public void testParseTimeZoneEndToEnd() {
14379268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        TimeZone tz = CalendarUtilities.tziStringToTimeZone(AMERICA_DAWSON_TIME);
14479268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        assertEquals("America/Dawson", tz.getID());
14579268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        tz = CalendarUtilities.tziStringToTimeZone(ASIA_CALCUTTA_TIME);
14679268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        assertEquals("Asia/Calcutta", tz.getID());
14779268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        tz = CalendarUtilities.tziStringToTimeZone(AUSTRALIA_ACT_TIME);
14879268e63dbda6ebc94d20e72e2bb1c245ee64678Marc Blank        assertEquals("Australia/ACT", tz.getID());
1492c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank
1507b873db28bda10f98f3782b7b161cb66117034d7Marc Blank        tz = CalendarUtilities.tziStringToTimeZone(EUROPE_LONDON_TIME_BY_NAME);
1517b873db28bda10f98f3782b7b161cb66117034d7Marc Blank        assertEquals("Europe/London", tz.getID());
1527b873db28bda10f98f3782b7b161cb66117034d7Marc Blank
1532c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        // Test peculiar MS sent EST data with and without lenient precision; send standard
1542c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        // precision + 1 (i.e. 1ms) to make sure the code doesn't automatically flip to lenient
1552c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        // when the tz isn't found
1562c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        tz = CalendarUtilities.tziStringToTimeZoneImpl(LENIENT_EASTERN_TIME,
1572c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank                CalendarUtilities.STANDARD_DST_PRECISION+1);
1582c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        assertEquals("America/Atikokan", tz.getID());
1592c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        tz = CalendarUtilities.tziStringToTimeZoneImpl(LENIENT_EASTERN_TIME,
1602c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank                CalendarUtilities.LENIENT_DST_PRECISION);
1612c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank        assertEquals("America/Detroit", tz.getID());
1622c7d44b182654120a98921cbc864be2d135c8fdaMarc Blank
163270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        tz = CalendarUtilities.tziStringToTimeZone(GMT_UNKNOWN_DAYLIGHT_TIME);
164270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        int bias = tz.getOffset(System.currentTimeMillis());
165270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        assertEquals(0, bias);
1664868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        // Make sure non-DST TZ's work properly when the tested zone is the default zone
1674868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        TimeZone.setDefault(TimeZone.getTimeZone("America/Phoenix"));
1685bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank        tz = CalendarUtilities.tziStringToTimeZone(ARIZONA_TIME);
1695bb2c4930c27c5385ff1baf033d1f1a97d770d14Marc Blank        assertEquals("America/Phoenix", tz.getID());
1704868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        TimeZone.setDefault(TimeZone.getTimeZone("Pacific/Honolulu"));
1714868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        tz = CalendarUtilities.tziStringToTimeZone(HAWAII_TIME);
1724868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        assertEquals("Pacific/Honolulu", tz.getID());
1734868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        // Make sure non-DST TZ's get the proper offset and DST status otherwise
1744868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        CalendarUtilities.clearTimeZoneCache();
1754868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        TimeZone azTime = TimeZone.getTimeZone("America/Phoenix");
1764868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
1774868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        tz = CalendarUtilities.tziStringToTimeZone(ARIZONA_TIME);
1784868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        assertFalse("America/Phoenix".equals(tz.getID()));
1794868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        assertFalse(tz.useDaylightTime());
1804868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        // It doesn't matter what time is passed in, since neither TZ has dst
1814868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        long now = System.currentTimeMillis();
1824868e0f09b58104741a5593f6097589ac62c6ce3Marc Blank        assertEquals(azTime.getOffset(now), tz.getOffset(now));
1835862a85e17e81866ca82a9905577931947fbd44eMarc Blank    }
1845862a85e17e81866ca82a9905577931947fbd44eMarc Blank
185f35b67cef20189c12a1a387dedf200eb30089725Marc Blank    public void testGenerateEasDayOfWeek() {
186b129a5f1b340ae6362397685c407099ceae8d9e9Marc Blank        String byDay = "TU,WE,SA";
187377230593dca7cb01483bfaf93959e5821f5f028Marc Blank        // TU = 4, WE = 8; SA = 64;
188f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertEquals("76", CalendarUtilities.generateEasDayOfWeek(byDay));
189377230593dca7cb01483bfaf93959e5821f5f028Marc Blank        // MO = 2, TU = 4; WE = 8; TH = 16; FR = 32
190b129a5f1b340ae6362397685c407099ceae8d9e9Marc Blank        byDay = "MO,TU,WE,TH,FR";
191f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertEquals("62", CalendarUtilities.generateEasDayOfWeek(byDay));
192377230593dca7cb01483bfaf93959e5821f5f028Marc Blank        // SU = 1
193f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        byDay = "SU";
194f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertEquals("1", CalendarUtilities.generateEasDayOfWeek(byDay));
195f35b67cef20189c12a1a387dedf200eb30089725Marc Blank    }
196f35b67cef20189c12a1a387dedf200eb30089725Marc Blank
197f35b67cef20189c12a1a387dedf200eb30089725Marc Blank    public void testTokenFromRrule() {
198f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        String rrule = "FREQ=DAILY;INTERVAL=1;BYDAY=WE,TH,SA;BYMONTHDAY=17";
199f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertEquals("DAILY", CalendarUtilities.tokenFromRrule(rrule, "FREQ="));
200f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertEquals("1", CalendarUtilities.tokenFromRrule(rrule, "INTERVAL="));
201f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertEquals("17", CalendarUtilities.tokenFromRrule(rrule, "BYMONTHDAY="));
202b129a5f1b340ae6362397685c407099ceae8d9e9Marc Blank        assertEquals("WE,TH,SA", CalendarUtilities.tokenFromRrule(rrule, "BYDAY="));
203f35b67cef20189c12a1a387dedf200eb30089725Marc Blank        assertNull(CalendarUtilities.tokenFromRrule(rrule, "UNTIL="));
204f35b67cef20189c12a1a387dedf200eb30089725Marc Blank    }
205f35b67cef20189c12a1a387dedf200eb30089725Marc Blank
20621c3c670ff6b932a4ecbeda230bb160178bdd957Marc Blank    public void testRecurrenceUntilToEasUntil() {
20760df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank        TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
20860df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank        // Case where local time crosses into next day in GMT
20960df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank        assertEquals("20110730T000000Z",
21060df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank                CalendarUtilities.recurrenceUntilToEasUntil("20110731T025959Z"));
21160df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank        // Case where local time does not cross into next day in GMT
21260df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank        assertEquals("20110730T000000Z",
21360df2ad267f1ad7aed45b583adcd1a5bb2a006b0Marc Blank                CalendarUtilities.recurrenceUntilToEasUntil("20110730T235959Z"));
21421c3c670ff6b932a4ecbeda230bb160178bdd957Marc Blank    }
21521c3c670ff6b932a4ecbeda230bb160178bdd957Marc Blank
216c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank    public void testParseEmailDateTimeToMillis(String date) {
217c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        // Format for email date strings is 2010-02-23T16:00:00.000Z
218c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        String dateString = "2010-02-23T15:16:17.000Z";
2198e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda        long dateTime = Utility.parseEmailDateTimeToMillis(dateString);
220c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        GregorianCalendar cal = new GregorianCalendar();
221c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        cal.setTimeInMillis(dateTime);
222c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
223c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.YEAR), 2010);
224c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.MONTH), 1);  // 0 based
225c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.DAY_OF_MONTH), 23);
226c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.HOUR_OF_DAY), 16);
227c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.MINUTE), 16);
228c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.SECOND), 17);
229c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank    }
230c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank
231c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank    public void testParseDateTimeToMillis(String date) {
232c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        // Format for calendar date strings is 20100223T160000000Z
233c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        String dateString = "20100223T151617000Z";
2348e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda        long dateTime = Utility.parseDateTimeToMillis(dateString);
235c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        GregorianCalendar cal = new GregorianCalendar();
236c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        cal.setTimeInMillis(dateTime);
237c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
238c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.YEAR), 2010);
239c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.MONTH), 1);  // 0 based
240c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.DAY_OF_MONTH), 23);
241c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.HOUR_OF_DAY), 16);
242c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.MINUTE), 16);
243c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank        assertEquals(cal.get(Calendar.SECOND), 17);
244c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank    }
24521c3c670ff6b932a4ecbeda230bb160178bdd957Marc Blank
2461c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    private Entity setupTestEventEntity(String organizer, String attendee, String title) {
2475c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Create an Entity for an Event
2485c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        ContentValues entityValues = new ContentValues();
2495c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        Entity entity = new Entity(entityValues);
2505c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
2515c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Set up values for the Event
2525c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        String location = "Meeting Location";
2535c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
2545c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Fill in times, location, title, and organizer
2555c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entityValues.put("DTSTAMP",
2565c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank                CalendarUtilities.convertEmailDateTimeToCalendarDateTime("2010-04-05T14:30:51Z"));
2575c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entityValues.put(Events.DTSTART,
2588e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda                Utility.parseEmailDateTimeToMillis("2010-04-12T18:30:00Z"));
2595c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entityValues.put(Events.DTEND,
2608e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda                Utility.parseEmailDateTimeToMillis("2010-04-12T19:30:00Z"));
2615c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entityValues.put(Events.EVENT_LOCATION, location);
2625c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entityValues.put(Events.TITLE, title);
2635c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entityValues.put(Events.ORGANIZER, organizer);
26404c880a6b5ad041f172d4b1eeecc06d6a06e4141RoboErik        entityValues.put(Events.SYNC_DATA2, "31415926535");
2655c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
2665c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Add the attendee
2675c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        ContentValues attendeeValues = new ContentValues();
2685c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
2695c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        attendeeValues.put(Attendees.ATTENDEE_EMAIL, attendee);
2705c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entity.addSubValue(Attendees.CONTENT_URI, attendeeValues);
2715c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
2725c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Add the organizer
2735c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        ContentValues organizerValues = new ContentValues();
2745c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        organizerValues.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER);
2755c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        organizerValues.put(Attendees.ATTENDEE_EMAIL, organizer);
2765c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        entity.addSubValue(Attendees.CONTENT_URI, organizerValues);
2771c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        return entity;
2781c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    }
2795c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
280dafc866120dac68fabbddcc9943e3995894c5244Marc Blank    private Entity setupTestExceptionEntity(String organizer, String attendee, String title) {
281dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        Entity entity = setupTestEventEntity(organizer, attendee, title);
282dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        ContentValues entityValues = entity.getEntityValues();
2839e86eb14c6e1f7d7730f8ca6953fdfd95fe2b143RoboErik        entityValues.put(Events.ORIGINAL_SYNC_ID, 69);
284dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // The exception will be on April 26th
285dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        entityValues.put(Events.ORIGINAL_INSTANCE_TIME,
286dafc866120dac68fabbddcc9943e3995894c5244Marc Blank                Utility.parseEmailDateTimeToMillis("2010-04-26T18:30:00Z"));
287dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        return entity;
288dafc866120dac68fabbddcc9943e3995894c5244Marc Blank    }
289dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
2901c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    public void testCreateMessageForEntity_Reply() {
2911c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // Set up the "event"
2921c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String title = "Discuss Unit Tests";
2939ca8918b82221dd441293973ffb84d565a52993aMarc Blank        Entity entity = setupTestEventEntity(ORGANIZER, ATTENDEE, title);
2945c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
2951c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // Create a dummy account for the attendee
2961c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        Account account = new Account();
2979ca8918b82221dd441293973ffb84d565a52993aMarc Blank        account.mEmailAddress = ATTENDEE;
2985c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
2991c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // The uid is required, but can be anything
3001c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String uid = "31415926535";
3011c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
3025c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Create the outgoing message
3035c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        Message msg = CalendarUtilities.createMessageForEntity(mContext, entity,
3045c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank                Message.FLAG_OUTGOING_MEETING_ACCEPT, uid, account);
3055c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
3065c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // First, we should have a message
3075c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        assertNotNull(msg);
3085c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
3095c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // Now check some of the fields of the message
3109ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertEquals(Address.pack(new Address[] {new Address(ORGANIZER)}), msg.mTo);
31100702b7e577ff2b7b1ae3f6dceefc615ba9cba72Marc Blank        Resources resources = getContext().getResources();
31200702b7e577ff2b7b1ae3f6dceefc615ba9cba72Marc Blank        String accept = resources.getString(R.string.meeting_accepted, title);
313820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals(accept, msg.mSubject);
31400702b7e577ff2b7b1ae3f6dceefc615ba9cba72Marc Blank        assertNotNull(msg.mText);
31500702b7e577ff2b7b1ae3f6dceefc615ba9cba72Marc Blank        assertTrue(msg.mText.contains(resources.getString(R.string.meeting_where, "")));
3165c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
3175c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // And make sure we have an attachment
3185c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        assertNotNull(msg.mAttachments);
3195c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        assertEquals(1, msg.mAttachments.size());
3205c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        Attachment att = msg.mAttachments.get(0);
3215c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        // And that the attachment has the correct elements
3225c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        assertEquals("invite.ics", att.mFileName);
323b4d217e5170ae397d741e95308d98e80d0c2f637Marc Blank        assertEquals(Attachment.FLAG_ICS_ALTERNATIVE_PART,
324b4d217e5170ae397d741e95308d98e80d0c2f637Marc Blank                att.mFlags & Attachment.FLAG_ICS_ALTERNATIVE_PART);
3255c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        assertEquals("text/calendar; method=REPLY", att.mMimeType);
326e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        assertNotNull(att.mContentBytes);
327e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        assertEquals(att.mSize, att.mContentBytes.length);
3285c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
3295c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank        //TODO Check the contents of the attachment using an iCalendar parser
3305c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank    }
3315c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank
332c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank    public void testCreateMessageForEntity_Invite_AllDay() throws IOException {
333c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // Set up the "event"
334c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        String title = "Discuss Unit Tests";
335c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        Entity entity = setupTestEventEntity(ORGANIZER, ATTENDEE, title);
33689bee1e3d03b439f4084bc9962bb3cbffd0b878aMarc Blank        ContentValues entityValues = entity.getEntityValues();
33789bee1e3d03b439f4084bc9962bb3cbffd0b878aMarc Blank        entityValues.put(Events.ALL_DAY, 1);
33889bee1e3d03b439f4084bc9962bb3cbffd0b878aMarc Blank        entityValues.put(Events.DURATION, "P1D");
33989bee1e3d03b439f4084bc9962bb3cbffd0b878aMarc Blank        entityValues.remove(Events.DTEND);
340c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
341c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // Create a dummy account for the attendee
342c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        Account account = new Account();
343c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        account.mEmailAddress = ORGANIZER;
344c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
345c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // The uid is required, but can be anything
346c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        String uid = "31415926535";
347c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
348c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // Create the outgoing message
349c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        Message msg = CalendarUtilities.createMessageForEntity(mContext, entity,
350c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank                Message.FLAG_OUTGOING_MEETING_INVITE, uid, account);
351c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
352c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // First, we should have a message
353c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNotNull(msg);
354c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
355c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // Now check some of the fields of the message
356c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(Address.pack(new Address[] {new Address(ATTENDEE)}), msg.mTo);
35746e18bd76629be0835a5d5e6c839b6daac6cdfdcMarc Blank        assertEquals(title, msg.mSubject);
358c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
359c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // And make sure we have an attachment
360c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNotNull(msg.mAttachments);
361c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(1, msg.mAttachments.size());
362c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        Attachment att = msg.mAttachments.get(0);
363c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // And that the attachment has the correct elements
364c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("invite.ics", att.mFileName);
365c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(Attachment.FLAG_ICS_ALTERNATIVE_PART,
366c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank                att.mFlags & Attachment.FLAG_ICS_ALTERNATIVE_PART);
367c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("text/calendar; method=REQUEST", att.mMimeType);
368c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNotNull(att.mContentBytes);
369c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(att.mSize, att.mContentBytes.length);
370c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
371c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // We'll check the contents of the ics file here
372c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        BlockHash vcalendar = parseIcsContent(att.mContentBytes);
373c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNotNull(vcalendar);
374c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
375c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // We should have a VCALENDAR with a REQUEST method
376c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("VCALENDAR", vcalendar.name);
377c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("REQUEST", vcalendar.get("METHOD"));
378c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
379c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // We should have one block under VCALENDAR
380c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(1, vcalendar.blocks.size());
381c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        BlockHash vevent = vcalendar.blocks.get(0);
382c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // It's a VEVENT with the following fields
383c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("VEVENT", vevent.name);
384c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("Meeting Location", vevent.get("LOCATION"));
385c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("0", vevent.get("SEQUENCE"));
386c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("Discuss Unit Tests", vevent.get("SUMMARY"));
387c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(uid, vevent.get("UID"));
388c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("MAILTO:" + ATTENDEE,
389c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank                vevent.get("ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE"));
390c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
391c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // These next two fields should have a date only
392c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("20100412", vevent.get("DTSTART;VALUE=DATE"));
39389bee1e3d03b439f4084bc9962bb3cbffd0b878aMarc Blank        assertEquals("20100413", vevent.get("DTEND;VALUE=DATE"));
394c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // This should be set to TRUE for all-day events
395c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals("TRUE", vevent.get("X-MICROSOFT-CDO-ALLDAYEVENT"));
396c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank    }
397c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
3981c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    public void testCreateMessageForEntity_Invite() throws IOException {
3991c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // Set up the "event"
4001c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String title = "Discuss Unit Tests";
4019ca8918b82221dd441293973ffb84d565a52993aMarc Blank        Entity entity = setupTestEventEntity(ORGANIZER, ATTENDEE, title);
4021c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4031c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // Create a dummy account for the attendee
4041c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        Account account = new Account();
4059ca8918b82221dd441293973ffb84d565a52993aMarc Blank        account.mEmailAddress = ORGANIZER;
4061c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4071c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // The uid is required, but can be anything
4081c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String uid = "31415926535";
4091c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4101c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // Create the outgoing message
4111c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        Message msg = CalendarUtilities.createMessageForEntity(mContext, entity,
4121c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                Message.FLAG_OUTGOING_MEETING_INVITE, uid, account);
4131c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4141c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // First, we should have a message
4151c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertNotNull(msg);
4161c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4171c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // Now check some of the fields of the message
4189ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertEquals(Address.pack(new Address[] {new Address(ATTENDEE)}), msg.mTo);
41946e18bd76629be0835a5d5e6c839b6daac6cdfdcMarc Blank        assertEquals(title, msg.mSubject);
4201c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4211c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // And make sure we have an attachment
4221c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertNotNull(msg.mAttachments);
4231c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals(1, msg.mAttachments.size());
4241c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        Attachment att = msg.mAttachments.get(0);
4251c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // And that the attachment has the correct elements
4261c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("invite.ics", att.mFileName);
427b4d217e5170ae397d741e95308d98e80d0c2f637Marc Blank        assertEquals(Attachment.FLAG_ICS_ALTERNATIVE_PART,
428b4d217e5170ae397d741e95308d98e80d0c2f637Marc Blank                att.mFlags & Attachment.FLAG_ICS_ALTERNATIVE_PART);
4291c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("text/calendar; method=REQUEST", att.mMimeType);
430e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        assertNotNull(att.mContentBytes);
431e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        assertEquals(att.mSize, att.mContentBytes.length);
4321c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4331c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // We'll check the contents of the ics file here
434e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        BlockHash vcalendar = parseIcsContent(att.mContentBytes);
4351c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertNotNull(vcalendar);
4361c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4371c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // We should have a VCALENDAR with a REQUEST method
4381c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("VCALENDAR", vcalendar.name);
4391c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("REQUEST", vcalendar.get("METHOD"));
4401c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
4411c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // We should have one block under VCALENDAR
4421c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals(1, vcalendar.blocks.size());
4431c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        BlockHash vevent = vcalendar.blocks.get(0);
4441c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        // It's a VEVENT with the following fields
4451c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("VEVENT", vevent.name);
4461c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("Meeting Location", vevent.get("LOCATION"));
4471c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("0", vevent.get("SEQUENCE"));
4481c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals("Discuss Unit Tests", vevent.get("SUMMARY"));
4491c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        assertEquals(uid, vevent.get("UID"));
4509ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertEquals("MAILTO:" + ATTENDEE,
4511c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                vevent.get("ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE"));
452c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank
453c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // These next two fields should exist (without the VALUE=DATE suffix)
454c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNotNull(vevent.get("DTSTART"));
455c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNotNull(vevent.get("DTEND"));
456c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNull(vevent.get("DTSTART;VALUE=DATE"));
457c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNull(vevent.get("DTEND;VALUE=DATE"));
458c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        // This shouldn't exist for this event
459c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertNull(vevent.get("X-MICROSOFT-CDO-ALLDAYEVENT"));
4601c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    }
4611c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
46267010d38cb168666b34431c96f331f36507e1f03Marc Blank    public void testCreateMessageForEntity_Recurring() throws IOException {
46367010d38cb168666b34431c96f331f36507e1f03Marc Blank        // Set up the "event"
46467010d38cb168666b34431c96f331f36507e1f03Marc Blank        String title = "Discuss Unit Tests";
46567010d38cb168666b34431c96f331f36507e1f03Marc Blank        Entity entity = setupTestEventEntity(ORGANIZER, ATTENDEE, title);
46667010d38cb168666b34431c96f331f36507e1f03Marc Blank        // Set up a RRULE for this event
46767010d38cb168666b34431c96f331f36507e1f03Marc Blank        entity.getEntityValues().put(Events.RRULE, "FREQ=DAILY");
46867010d38cb168666b34431c96f331f36507e1f03Marc Blank
46967010d38cb168666b34431c96f331f36507e1f03Marc Blank        // Create a dummy account for the attendee
47067010d38cb168666b34431c96f331f36507e1f03Marc Blank        Account account = new Account();
47167010d38cb168666b34431c96f331f36507e1f03Marc Blank        account.mEmailAddress = ORGANIZER;
47267010d38cb168666b34431c96f331f36507e1f03Marc Blank
47367010d38cb168666b34431c96f331f36507e1f03Marc Blank        // The uid is required, but can be anything
47467010d38cb168666b34431c96f331f36507e1f03Marc Blank        String uid = "31415926535";
47567010d38cb168666b34431c96f331f36507e1f03Marc Blank
47667010d38cb168666b34431c96f331f36507e1f03Marc Blank        // Create the outgoing message
47767010d38cb168666b34431c96f331f36507e1f03Marc Blank        Message msg = CalendarUtilities.createMessageForEntity(mContext, entity,
47867010d38cb168666b34431c96f331f36507e1f03Marc Blank                Message.FLAG_OUTGOING_MEETING_INVITE, uid, account);
47967010d38cb168666b34431c96f331f36507e1f03Marc Blank
48067010d38cb168666b34431c96f331f36507e1f03Marc Blank        // First, we should have a message
48167010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNotNull(msg);
48267010d38cb168666b34431c96f331f36507e1f03Marc Blank
48367010d38cb168666b34431c96f331f36507e1f03Marc Blank        // Now check some of the fields of the message
48467010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(Address.pack(new Address[] {new Address(ATTENDEE)}), msg.mTo);
48567010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(title, msg.mSubject);
48667010d38cb168666b34431c96f331f36507e1f03Marc Blank
48767010d38cb168666b34431c96f331f36507e1f03Marc Blank        // And make sure we have an attachment
48867010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNotNull(msg.mAttachments);
48967010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(1, msg.mAttachments.size());
49067010d38cb168666b34431c96f331f36507e1f03Marc Blank        Attachment att = msg.mAttachments.get(0);
49167010d38cb168666b34431c96f331f36507e1f03Marc Blank        // And that the attachment has the correct elements
49267010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("invite.ics", att.mFileName);
49367010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(Attachment.FLAG_ICS_ALTERNATIVE_PART,
49467010d38cb168666b34431c96f331f36507e1f03Marc Blank                att.mFlags & Attachment.FLAG_ICS_ALTERNATIVE_PART);
49567010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("text/calendar; method=REQUEST", att.mMimeType);
49667010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNotNull(att.mContentBytes);
49767010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(att.mSize, att.mContentBytes.length);
49867010d38cb168666b34431c96f331f36507e1f03Marc Blank
49967010d38cb168666b34431c96f331f36507e1f03Marc Blank        // We'll check the contents of the ics file here
50067010d38cb168666b34431c96f331f36507e1f03Marc Blank        BlockHash vcalendar = parseIcsContent(att.mContentBytes);
50167010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNotNull(vcalendar);
50267010d38cb168666b34431c96f331f36507e1f03Marc Blank
50367010d38cb168666b34431c96f331f36507e1f03Marc Blank        // We should have a VCALENDAR with a REQUEST method
50467010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("VCALENDAR", vcalendar.name);
50567010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("REQUEST", vcalendar.get("METHOD"));
50667010d38cb168666b34431c96f331f36507e1f03Marc Blank
50767010d38cb168666b34431c96f331f36507e1f03Marc Blank        // We should have two blocks under VCALENDAR (VTIMEZONE and VEVENT)
50867010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(2, vcalendar.blocks.size());
50967010d38cb168666b34431c96f331f36507e1f03Marc Blank
51067010d38cb168666b34431c96f331f36507e1f03Marc Blank        // This is the time zone that should be used
51167010d38cb168666b34431c96f331f36507e1f03Marc Blank        TimeZone timeZone = TimeZone.getDefault();
51267010d38cb168666b34431c96f331f36507e1f03Marc Blank
51367010d38cb168666b34431c96f331f36507e1f03Marc Blank        BlockHash vtimezone = vcalendar.blocks.get(0);
51467010d38cb168666b34431c96f331f36507e1f03Marc Blank        // It should be a VTIMEZONE for timeZone
51567010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("VTIMEZONE", vtimezone.name);
51667010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(timeZone.getID(), vtimezone.get("TZID"));
51767010d38cb168666b34431c96f331f36507e1f03Marc Blank
51867010d38cb168666b34431c96f331f36507e1f03Marc Blank        BlockHash vevent = vcalendar.blocks.get(1);
51967010d38cb168666b34431c96f331f36507e1f03Marc Blank        // It's a VEVENT with the following fields
52067010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("VEVENT", vevent.name);
52167010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("Meeting Location", vevent.get("LOCATION"));
52267010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("0", vevent.get("SEQUENCE"));
52367010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("Discuss Unit Tests", vevent.get("SUMMARY"));
52467010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals(uid, vevent.get("UID"));
52567010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertEquals("MAILTO:" + ATTENDEE,
52667010d38cb168666b34431c96f331f36507e1f03Marc Blank                vevent.get("ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE"));
52767010d38cb168666b34431c96f331f36507e1f03Marc Blank
52867010d38cb168666b34431c96f331f36507e1f03Marc Blank        // We should have DTSTART/DTEND with time zone
52967010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNotNull(vevent.get("DTSTART;TZID=" + timeZone.getID()));
53067010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNotNull(vevent.get("DTEND;TZID=" + timeZone.getID()));
53167010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNull(vevent.get("DTSTART"));
53267010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNull(vevent.get("DTEND"));
53367010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNull(vevent.get("DTSTART;VALUE=DATE"));
53467010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNull(vevent.get("DTEND;VALUE=DATE"));
53567010d38cb168666b34431c96f331f36507e1f03Marc Blank        // This shouldn't exist for this event
53667010d38cb168666b34431c96f331f36507e1f03Marc Blank        assertNull(vevent.get("X-MICROSOFT-CDO-ALLDAYEVENT"));
53767010d38cb168666b34431c96f331f36507e1f03Marc Blank    }
53867010d38cb168666b34431c96f331f36507e1f03Marc Blank
539dafc866120dac68fabbddcc9943e3995894c5244Marc Blank    public void testCreateMessageForEntity_Exception_Cancel() throws IOException {
540dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // Set up the "exception"...
541dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        String title = "Discuss Unit Tests";
5429ca8918b82221dd441293973ffb84d565a52993aMarc Blank        Entity entity = setupTestExceptionEntity(ORGANIZER, ATTENDEE, title);
543dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
544dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        ContentValues entityValues = entity.getEntityValues();
545dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // Mark the Exception as dirty
5469e86eb14c6e1f7d7730f8ca6953fdfd95fe2b143RoboErik        entityValues.put(Events.DIRTY, 1);
547dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // And mark it canceled
548dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        entityValues.put(Events.STATUS, Events.STATUS_CANCELED);
549dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
550dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // Create a dummy account for the attendee
551dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        Account account = new Account();
5529ca8918b82221dd441293973ffb84d565a52993aMarc Blank        account.mEmailAddress = ORGANIZER;
553dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
554dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // The uid is required, but can be anything
555dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        String uid = "31415926535";
556dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
557dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // Create the outgoing message
558dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        Message msg = CalendarUtilities.createMessageForEntity(mContext, entity,
559bf1de871b7ec63c93694ba022282e8789e69f201Marc Blank                Message.FLAG_OUTGOING_MEETING_CANCEL, uid, account);
560dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
561dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // First, we should have a message
562dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertNotNull(msg);
563dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
564dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // Now check some of the fields of the message
5659ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertEquals(Address.pack(new Address[] {new Address(ATTENDEE)}), msg.mTo);
566bf1de871b7ec63c93694ba022282e8789e69f201Marc Blank        String cancel = getContext().getResources().getString(R.string.meeting_canceled, title);
567bf1de871b7ec63c93694ba022282e8789e69f201Marc Blank        assertEquals(cancel, msg.mSubject);
568dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
569dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // And make sure we have an attachment
570dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertNotNull(msg.mAttachments);
571dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals(1, msg.mAttachments.size());
572dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        Attachment att = msg.mAttachments.get(0);
573dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // And that the attachment has the correct elements
574dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("invite.ics", att.mFileName);
575b4d217e5170ae397d741e95308d98e80d0c2f637Marc Blank        assertEquals(Attachment.FLAG_ICS_ALTERNATIVE_PART,
576b4d217e5170ae397d741e95308d98e80d0c2f637Marc Blank                att.mFlags & Attachment.FLAG_ICS_ALTERNATIVE_PART);
577f58e3ba6e6e246a804e6908c831a43b46a61bc07Marc Blank        assertEquals("text/calendar; method=CANCEL", att.mMimeType);
578e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        assertNotNull(att.mContentBytes);
579dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
580dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // We'll check the contents of the ics file here
581e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki        BlockHash vcalendar = parseIcsContent(att.mContentBytes);
582dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertNotNull(vcalendar);
583dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
584f58e3ba6e6e246a804e6908c831a43b46a61bc07Marc Blank        // We should have a VCALENDAR with a CANCEL method
585dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("VCALENDAR", vcalendar.name);
586f58e3ba6e6e246a804e6908c831a43b46a61bc07Marc Blank        assertEquals("CANCEL", vcalendar.get("METHOD"));
587dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
588dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // This is the time zone that should be used
589dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        TimeZone timeZone = TimeZone.getDefault();
590dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
591dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // We should have two blocks under VCALENDAR (VTIMEZONE and VEVENT)
592dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals(2, vcalendar.blocks.size());
593dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
594dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        BlockHash vtimezone = vcalendar.blocks.get(0);
595dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // It should be a VTIMEZONE for timeZone
596dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("VTIMEZONE", vtimezone.name);
597dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals(timeZone.getID(), vtimezone.get("TZID"));
598dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
599dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        BlockHash vevent = vcalendar.blocks.get(1);
600dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // It's a VEVENT with the following fields
601dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("VEVENT", vevent.name);
602dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("Meeting Location", vevent.get("LOCATION"));
603dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("0", vevent.get("SEQUENCE"));
604dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals("Discuss Unit Tests", vevent.get("SUMMARY"));
605dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertEquals(uid, vevent.get("UID"));
6069ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertEquals("MAILTO:" + ATTENDEE,
607bf1de871b7ec63c93694ba022282e8789e69f201Marc Blank                vevent.get("ATTENDEE;ROLE=REQ-PARTICIPANT"));
608dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        long originalTime = entityValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME);
609dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        assertNotSame(0, originalTime);
610dafc866120dac68fabbddcc9943e3995894c5244Marc Blank        // For an exception, RECURRENCE-ID is critical
611c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank        assertEquals(CalendarUtilities.millisToEasDateTime(originalTime, timeZone,
612c1b63a27ed05bed70f74cf33fe08f8bd1f0d745fMarc Blank                true /*withTime*/), vevent.get("RECURRENCE-ID" + ";TZID=" + timeZone.getID()));
613dafc866120dac68fabbddcc9943e3995894c5244Marc Blank    }
614dafc866120dac68fabbddcc9943e3995894c5244Marc Blank
615820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank    public void testUtcOffsetString() {
616820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals(CalendarUtilities.utcOffsetString(540), "+0900");
617820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals(CalendarUtilities.utcOffsetString(-480), "-0800");
618820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals(CalendarUtilities.utcOffsetString(0), "+0000");
619820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank    }
6201c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
621820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank    public void testFindTransitionDate() {
622820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // We'll find some transitions and make sure that we're properly in or out of daylight time
623820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // on either side of the transition.
624820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Use CST for testing (any other will do as well, as long as it has DST)
625820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        TimeZone tz = TimeZone.getTimeZone("US/Central");
626ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        // Confirm that this time zone uses DST
627ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        assertTrue(tz.useDaylightTime());
628820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Get a calendar at January 1st of the current year
629820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        GregorianCalendar calendar = new GregorianCalendar(tz);
630820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        calendar.set(CalendarUtilities.sCurrentYear, Calendar.JANUARY, 1);
631820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Get start and end times at start and end of year
632820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        long startTime = calendar.getTimeInMillis();
633820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        long endTime = startTime + (365*CalendarUtilities.DAYS);
634820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Find the first transition
635820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        GregorianCalendar transitionCalendar =
636820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank            CalendarUtilities.findTransitionDate(tz, startTime, endTime, false);
637820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        long transitionTime = transitionCalendar.getTimeInMillis();
638820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Before should be in standard time; after in daylight time
639820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        Date beforeDate = new Date(transitionTime - CalendarUtilities.HOURS);
640820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        Date afterDate = new Date(transitionTime + CalendarUtilities.HOURS);
641820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertFalse(tz.inDaylightTime(beforeDate));
642820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertTrue(tz.inDaylightTime(afterDate));
643820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank
644820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Find the next one...
645820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        transitionCalendar = CalendarUtilities.findTransitionDate(tz, transitionTime +
646820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank                CalendarUtilities.DAYS, endTime, true);
647820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        transitionTime = transitionCalendar.getTimeInMillis();
648820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // This time, Before should be in daylight time; after in standard time
649820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        beforeDate = new Date(transitionTime - CalendarUtilities.HOURS);
650820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        afterDate = new Date(transitionTime + CalendarUtilities.HOURS);
651820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertTrue(tz.inDaylightTime(beforeDate));
652820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertFalse(tz.inDaylightTime(afterDate));
653820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank
654ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        // Kinshasa has no daylight savings time
655ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        tz = TimeZone.getTimeZone("Africa/Kinshasa");
656ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        // Confirm that there's no DST for this time zone
657ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        assertFalse(tz.useDaylightTime());
658820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Get a calendar at January 1st of the current year
659820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        calendar = new GregorianCalendar(tz);
660820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        calendar.set(CalendarUtilities.sCurrentYear, Calendar.JANUARY, 1);
661820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Get start and end times at start and end of year
662820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        startTime = calendar.getTimeInMillis();
663820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        endTime = startTime + (365*CalendarUtilities.DAYS);
664820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Find the first transition
665820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        transitionCalendar = CalendarUtilities.findTransitionDate(tz, startTime, endTime, false);
666820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // There had better not be one
667820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertNull(transitionCalendar);
668820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank    }
669377230593dca7cb01483bfaf93959e5821f5f028Marc Blank
670820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank    public void testRruleFromRecurrence() {
671820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Every Monday for 2 weeks
672820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        String rrule = CalendarUtilities.rruleFromRecurrence(
673820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank                1 /*Weekly*/, 2 /*Occurrences*/, 1 /*Interval*/, 2 /*Monday*/, 0, 0, 0, null);
674f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        assertEquals("FREQ=WEEKLY;COUNT=2;INTERVAL=1;BYDAY=MO", rrule);
675820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Every Tuesday and Friday
676820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
677820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank                1 /*Weekly*/, 0 /*Occurrences*/, 0 /*Interval*/, 36 /*Tue&Fri*/, 0, 0, 0, null);
678820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals("FREQ=WEEKLY;BYDAY=TU,FR", rrule);
679820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // The last Saturday of the month
680820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
681f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank                1 /*Weekly*/, 0, 0, 64 /*Sat*/, 0, 5 /*Last*/, 0, null);
682f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        assertEquals("FREQ=WEEKLY;BYDAY=-1SA", rrule);
683820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // The third Wednesday and Thursday of the month
684820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
685f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank                1 /*Weekly*/, 0, 0, 24 /*Wed&Thu*/, 0, 3 /*3rd*/, 0, null);
686f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        assertEquals("FREQ=WEEKLY;BYDAY=3WE,3TH", rrule);
687f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
688f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank                3 /*Monthly/Day*/, 0, 0, 127 /*LastDay*/, 0, 0, 0, null);
689f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        assertEquals("FREQ=MONTHLY;BYMONTHDAY=-1", rrule);
690f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
691f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank                3 /*Monthly/Day*/, 0, 0, 62 /*M-F*/, 0, 5 /*Last week*/, 0, null);
692f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        assertEquals("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1", rrule);
693820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // The 14th of the every month
694820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
695820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank                2 /*Monthly/Date*/, 0, 0, 0, 14 /*14th*/, 0, 0, null);
696820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals("FREQ=MONTHLY;BYMONTHDAY=14", rrule);
697820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // Every 31st of October
698820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
699820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank                5 /*Yearly/Date*/, 0, 0, 0, 31 /*31st*/, 0, 10 /*October*/, null);
700820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals("FREQ=YEARLY;BYMONTHDAY=31;BYMONTH=10", rrule);
701820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        // The first Tuesday of June
702820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        rrule = CalendarUtilities.rruleFromRecurrence(
703820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank                6 /*Yearly/Month/DayOfWeek*/, 0, 0, 4 /*Tue*/, 0, 1 /*1st*/, 6 /*June*/, null);
704820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank        assertEquals("FREQ=YEARLY;BYDAY=1TU;BYMONTH=6", rrule);
705820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank    }
706820dbc5ff3497fdd98fdb1cc42c1d298f9c1f199Marc Blank
7071c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    /**
708f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * Given a CalendarSyncAdapter and an RRULE, serialize the RRULE via recurrentFromRrule and
709f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * then parse the result.  Assert that the resulting RRULE is the same as the original.
710f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * @param adapter a CalendarSyncAdapter
711f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * @param rrule an RRULE string that will be tested
712f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * @throws IOException
713f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     */
714f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank    private void testSingleRecurrenceFromRrule(CalendarSyncAdapter adapter, String rrule)
715f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank            throws IOException {
716f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        Serializer s = new Serializer();
717f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        CalendarUtilities.recurrenceFromRrule(rrule, 0, s);
718f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        s.done();
719f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        EasCalendarSyncParser parser = adapter.new EasCalendarSyncParser(
720f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank                new ByteArrayInputStream(s.toByteArray()), adapter);
721f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        // The first element should be the outer CALENDAR_RECURRENCE tag
722f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        assertEquals(Tags.CALENDAR_RECURRENCE, parser.nextTag(Parser.START_DOCUMENT));
723f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        assertEquals(rrule, parser.recurrenceParser());
724f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank    }
725f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank
726f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank    /**
727f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * Round-trip test of RRULE handling; we serialize an RRULE and then parse the result; the
728f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     * result should be identical to the original RRULE
729f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank     */
730f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank    public void testRecurrenceFromRrule() throws IOException {
731f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        // A test sync adapter we can use throughout the test
732f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        CalendarSyncAdapter adapter = getTestSyncAdapter(CalendarSyncAdapter.class);
733f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank
734f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=WEEKLY;COUNT=2;INTERVAL=1;BYDAY=MO");
735f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=WEEKLY;BYDAY=TU,FR");
736f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=WEEKLY;BYDAY=-1SA");
737f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=WEEKLY;BYDAY=3WE,3TH");
738f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1");
739f352bc9f29cacc61b195069e48d5c8b868660694Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=MONTHLY;BYMONTHDAY=17");
740f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=YEARLY;BYMONTHDAY=31;BYMONTH=10");
741f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank        testSingleRecurrenceFromRrule(adapter, "FREQ=YEARLY;BYDAY=1TU;BYMONTH=6");
742f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank    }
743f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank
744f72b4bc0ea714067ae528e63c916f0237d2202e6Marc Blank    /**
7451c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     * For debugging purposes, to help keep track of parsing errors.
7461c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     */
7471c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    private class UnterminatedBlockException extends IOException {
7481c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        private static final long serialVersionUID = 1L;
7491c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        UnterminatedBlockException(String name) {
7501c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            super(name);
7511c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        }
7521c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    }
7531c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
7541c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    /**
7551c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     * A lightweight representation of block object containing a hash of individual values and an
7561c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     * array of inner blocks.  The object is build by pulling elements from a BufferedReader.
7571c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     * NOTE: Multiple values of a given field are not supported.  We'd see this with ATTENDEEs, for
7581c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     * example, and possibly RDATEs in VTIMEZONEs without an RRULE; these cases will be handled
7591c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     * at a later time.
7601c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank     */
7611c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    private class BlockHash {
7621c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String name;
7631c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        HashMap<String, String> hash = new HashMap<String, String>();
7641c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        ArrayList<BlockHash> blocks = new ArrayList<BlockHash>();
7651c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
7661c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        BlockHash (String _name, BufferedReader reader) throws IOException {
7671c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            name = _name;
7681c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            String lastField = null;
7691c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            String lastValue = null;
7701c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            while (true) {
7711c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                // Get a line; we're done if it's null
7721c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                String line = reader.readLine();
7731c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                if (line == null) {
7741c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    throw new UnterminatedBlockException(name);
7751c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                }
7761c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                int length = line.length();
7771c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                if (length == 0) {
7781c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    // We shouldn't ever see an empty line
7791c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    throw new IllegalArgumentException();
7801c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                }
781bc0c8c1523fc0bc42eb82dba6c2977492e441c03Makoto Onuki                // A line starting with tab is a continuation
782bc0c8c1523fc0bc42eb82dba6c2977492e441c03Makoto Onuki                if (line.charAt(0) == '\t') {
7831c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    // Remember the line and length
7841c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    lastValue = line.substring(1);
7851c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    // Save the concatenation of old and new values
7861c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    hash.put(lastField, hash.get(lastField) + lastValue);
7871c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    continue;
7881c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                }
7891c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                // Find the field delimiter
7901c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                int pos = line.indexOf(':');
7911c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                // If not found, or at EOL, this is a bad ics
7921c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                if (pos < 0 || pos >= length) {
7931c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    throw new IllegalArgumentException();
7941c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                }
7951c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                // Remember the field, value, and length
7961c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                lastField = line.substring(0, pos);
7971c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                lastValue = line.substring(pos + 1);
7981c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                if (lastField.equals("BEGIN")) {
7991c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    blocks.add(new BlockHash(lastValue, reader));
8001c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    continue;
8011c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                } else if (lastField.equals("END")) {
8021c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    if (!lastValue.equals(name)) {
8031c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                        throw new UnterminatedBlockException(name);
8041c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    }
8051c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                    break;
8061c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                }
8071c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
8081c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                // Save it away and continue
8091c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank                hash.put(lastField, lastValue);
8101c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            }
8111c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        }
8121c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
8131c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String get(String field) {
8141c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            return hash.get(field);
8151c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        }
8161c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    }
8171c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
818e54b75dc4f638e594e9b97e3b4ed8829fbc9b521Makoto Onuki    private BlockHash parseIcsContent(byte[] bytes) throws IOException {
819d38b4e9c1893a6c4ac70c08c5211629da3840cb2Makoto Onuki        BufferedReader reader = new BufferedReader(new StringReader(Utility.fromUtf8(bytes)));
8201c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        String line = reader.readLine();
8211c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        if (!line.equals("BEGIN:VCALENDAR")) {
8221c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank            throw new IllegalArgumentException();
8231c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        }
8241c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank        return new BlockHash("VCALENDAR", reader);
8251c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank    }
8261c9dfc1ce8d40b4a09fb6baf2fd9ed9c482b8900Marc Blank
8279ca8918b82221dd441293973ffb84d565a52993aMarc Blank    public void testBuildMessageTextFromEntityValues() {
8289ca8918b82221dd441293973ffb84d565a52993aMarc Blank        // Set up a test event
8299ca8918b82221dd441293973ffb84d565a52993aMarc Blank        String title = "Event Title";
8309ca8918b82221dd441293973ffb84d565a52993aMarc Blank        Entity entity = setupTestEventEntity(ORGANIZER, ATTENDEE, title);
8319ca8918b82221dd441293973ffb84d565a52993aMarc Blank        ContentValues entityValues = entity.getEntityValues();
8329ca8918b82221dd441293973ffb84d565a52993aMarc Blank
8339ca8918b82221dd441293973ffb84d565a52993aMarc Blank        // Save this away; we'll use it a few times below
8349ca8918b82221dd441293973ffb84d565a52993aMarc Blank        Resources resources = mContext.getResources();
8359ca8918b82221dd441293973ffb84d565a52993aMarc Blank        Date date = new Date(entityValues.getAsLong(Events.DTSTART));
8369ca8918b82221dd441293973ffb84d565a52993aMarc Blank        String dateTimeString = DateFormat.getDateTimeInstance().format(date);
8379ca8918b82221dd441293973ffb84d565a52993aMarc Blank
8389ca8918b82221dd441293973ffb84d565a52993aMarc Blank        // Get the text for this message
8399ca8918b82221dd441293973ffb84d565a52993aMarc Blank        StringBuilder sb = new StringBuilder();
8409ca8918b82221dd441293973ffb84d565a52993aMarc Blank        CalendarUtilities.buildMessageTextFromEntityValues(mContext, entityValues, sb);
8419ca8918b82221dd441293973ffb84d565a52993aMarc Blank        String text = sb.toString();
8429ca8918b82221dd441293973ffb84d565a52993aMarc Blank        // We'll just check the when and where
8439ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertTrue(text.contains(resources.getString(R.string.meeting_when, dateTimeString)));
8449ca8918b82221dd441293973ffb84d565a52993aMarc Blank        String location = entityValues.getAsString(Events.EVENT_LOCATION);
8459ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertTrue(text.contains(resources.getString(R.string.meeting_where, location)));
8469ca8918b82221dd441293973ffb84d565a52993aMarc Blank
8479ca8918b82221dd441293973ffb84d565a52993aMarc Blank        // Make this event recurring
8489ca8918b82221dd441293973ffb84d565a52993aMarc Blank        entity.getEntityValues().put(Events.RRULE, "FREQ=WEEKLY;BYDAY=MO");
8499ca8918b82221dd441293973ffb84d565a52993aMarc Blank        sb = new StringBuilder();
8509ca8918b82221dd441293973ffb84d565a52993aMarc Blank        CalendarUtilities.buildMessageTextFromEntityValues(mContext, entityValues, sb);
8519ca8918b82221dd441293973ffb84d565a52993aMarc Blank        text = sb.toString();
8529ca8918b82221dd441293973ffb84d565a52993aMarc Blank        assertTrue(text.contains(resources.getString(R.string.meeting_recurring, dateTimeString)));
8539ca8918b82221dd441293973ffb84d565a52993aMarc Blank    }
8549ca8918b82221dd441293973ffb84d565a52993aMarc Blank
855879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank    /**
856879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank     * Sanity test for time zone generation.  Most important, make sure that we can run through
857879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank     * all of the time zones without generating an exception.  Second, make sure that we're finding
858879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank     * rules for at least 90% of time zones that use daylight time (empirically, it's more like
859879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank     * 95%).  Log those without rules.
860879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank     * @throws IOException
861879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank     */
862879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank    public void testTimeZoneToVTimezone() throws IOException {
863879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        SimpleIcsWriter writer = new SimpleIcsWriter();
864879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        int rule = 0;
865879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        int nodst = 0;
866879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        int norule = 0;
867879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        ArrayList<String> norulelist = new ArrayList<String>();
868879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        for (String tzs: TimeZone.getAvailableIDs()) {
869879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            TimeZone tz = TimeZone.getTimeZone(tzs);
870879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            writer = new SimpleIcsWriter();
871879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            CalendarUtilities.timeZoneToVTimezone(tz, writer);
872879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            String vc = writer.toString();
873879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            boolean hasRule = vc.indexOf("RRULE") > 0;
874879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            if (hasRule) {
875879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank                rule++;
876879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            } else if (tz.useDaylightTime()) {
877879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank                norule++;
878879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank                norulelist.add(tz.getID());
879879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            } else {
880879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank                nodst++;
881879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            }
882879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        }
883879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        Log.d("TimeZoneGeneration",
884879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank                "Rule: " + rule + ", No DST: " + nodst + ", No rule: " + norule);
885879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        for (String nr: norulelist) {
886879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank            Log.d("TimeZoneGeneration", "No rule: " + nr);
887879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank        }
888ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        // This is an empirical sanity test; we shouldn't have too many time zones with DST and
889ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        // without a rule.
890ee8c9a284b160f353e3bd918aafbc7ed528bfc4cMarc Blank        assertTrue(norule < rule/8);
891879b4d7bd28de1c8bb8c8aba9c1ab17936d300aaMarc Blank    }
892b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank
893b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank    public void testGetUidFromGlobalObjId() {
894b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        // This is a "foreign" uid (from some vCalendar client)
895b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        String globalObjId = "BAAAAIIA4AB0xbcQGoLgCAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAA" +
896b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank                "HZDYWwtVWlkAQAAADI3NjU1NmRkLTg1MzAtNGZiZS1iMzE0LThiM2JlYTYwMjE0OQA=";
897b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        String uid = CalendarUtilities.getUidFromGlobalObjId(globalObjId);
898b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        assertEquals(uid, "276556dd-8530-4fbe-b314-8b3bea602149");
899b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        // This is a native EAS uid
900b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        globalObjId =
901b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank            "BAAAAIIA4AB0xbcQGoLgCAAAAADACTu7KbPKAQAAAAAAAAAAEAAAAObgsG6HVt1Fmy+7GlLbGhY=";
902b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        uid = CalendarUtilities.getUidFromGlobalObjId(globalObjId);
903b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank        assertEquals(uid, "040000008200E00074C5B7101A82E00800000000C0093BBB29B3CA" +
904b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank                "01000000000000000010000000E6E0B06E8756DD459B2FBB1A52DB1A16");
905b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank    }
906dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank
907dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank    public void testSelfAttendeeStatusFromBusyStatus() {
908dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(Attendees.ATTENDEE_STATUS_ACCEPTED,
909edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.attendeeStatusFromBusyStatus(
910dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        CalendarUtilities.BUSY_STATUS_BUSY));
911dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(Attendees.ATTENDEE_STATUS_TENTATIVE,
912edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.attendeeStatusFromBusyStatus(
913dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        CalendarUtilities.BUSY_STATUS_TENTATIVE));
914dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(Attendees.ATTENDEE_STATUS_NONE,
915edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.attendeeStatusFromBusyStatus(
916dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        CalendarUtilities.BUSY_STATUS_FREE));
917dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(Attendees.ATTENDEE_STATUS_NONE,
918edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.attendeeStatusFromBusyStatus(
919e51fedc3c055588a69da56d0b818ea12ed8f706fMarc Blank                        CalendarUtilities.BUSY_STATUS_OUT_OF_OFFICE));
920dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank    }
921dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank
922dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank    public void testBusyStatusFromSelfStatus() {
923dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(CalendarUtilities.BUSY_STATUS_FREE,
924edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.busyStatusFromAttendeeStatus(
925dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        Attendees.ATTENDEE_STATUS_DECLINED));
926e51fedc3c055588a69da56d0b818ea12ed8f706fMarc Blank        assertEquals(CalendarUtilities.BUSY_STATUS_FREE,
927edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.busyStatusFromAttendeeStatus(
928dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        Attendees.ATTENDEE_STATUS_NONE));
929e51fedc3c055588a69da56d0b818ea12ed8f706fMarc Blank        assertEquals(CalendarUtilities.BUSY_STATUS_FREE,
930edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.busyStatusFromAttendeeStatus(
931dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        Attendees.ATTENDEE_STATUS_INVITED));
932dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(CalendarUtilities.BUSY_STATUS_TENTATIVE,
933edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.busyStatusFromAttendeeStatus(
934dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        Attendees.ATTENDEE_STATUS_TENTATIVE));
935dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank        assertEquals(CalendarUtilities.BUSY_STATUS_BUSY,
936edcfd554728a27a7678306ea7432bd8676ec6990Marc Blank                CalendarUtilities.busyStatusFromAttendeeStatus(
937dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank                        Attendees.ATTENDEE_STATUS_ACCEPTED));
938dc27937dda50f991de9e12b98b80ee6aa3fe348eMarc Blank    }
939270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
940270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    public void testGetUtcAllDayCalendarTime() {
941270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        GregorianCalendar correctUtc = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
942270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        correctUtc.set(2011, 2, 10, 0, 0, 0);
943270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        long correctUtcTime = correctUtc.getTimeInMillis();
944270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
945270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        TimeZone localTimeZone = TimeZone.getTimeZone("GMT-0700");
946270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        GregorianCalendar localCalendar = new GregorianCalendar(localTimeZone);
947270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        localCalendar.set(2011, 2, 10, 12, 23, 34);
948270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        long localTimeMillis = localCalendar.getTimeInMillis();
949270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        long convertedUtcTime =
950270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank            CalendarUtilities.getUtcAllDayCalendarTime(localTimeMillis, localTimeZone);
951270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        // Milliseconds aren't zeroed out and may not be the same
952270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        assertEquals(convertedUtcTime/1000, correctUtcTime/1000);
953270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
954270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        localTimeZone = TimeZone.getTimeZone("GMT+0700");
955270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        localCalendar = new GregorianCalendar(localTimeZone);
956270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        localCalendar.set(2011, 2, 10, 12, 23, 34);
957270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        localTimeMillis = localCalendar.getTimeInMillis();
958270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        convertedUtcTime =
959270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank            CalendarUtilities.getUtcAllDayCalendarTime(localTimeMillis, localTimeZone);
960270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        assertEquals(convertedUtcTime/1000, correctUtcTime/1000);
961270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    }
962270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
963270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    public void testGetLocalAllDayCalendarTime() {
964270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
965270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        TimeZone localTimeZone = TimeZone.getTimeZone("GMT-0700");
966270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        GregorianCalendar correctLocal = new GregorianCalendar(localTimeZone);
967270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        correctLocal.set(2011, 2, 10, 0, 0, 0);
968270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        long correctLocalTime = correctLocal.getTimeInMillis();
969270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
970270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        GregorianCalendar utcCalendar = new GregorianCalendar(utcTimeZone);
971270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        utcCalendar.set(2011, 2, 10, 12, 23, 34);
972270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        long utcTimeMillis = utcCalendar.getTimeInMillis();
973270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        long convertedLocalTime =
974270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank            CalendarUtilities.getLocalAllDayCalendarTime(utcTimeMillis, localTimeZone);
975270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        // Milliseconds aren't zeroed out and may not be the same
976270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        assertEquals(convertedLocalTime/1000, correctLocalTime/1000);
977270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
978270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        localTimeZone = TimeZone.getTimeZone("GMT+0700");
979270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        correctLocal = new GregorianCalendar(localTimeZone);
980270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        correctLocal.set(2011, 2, 10, 0, 0, 0);
981270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        correctLocalTime = correctLocal.getTimeInMillis();
982270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank
983270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        utcCalendar = new GregorianCalendar(utcTimeZone);
984270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        utcCalendar.set(2011, 2, 10, 12, 23, 34);
985270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        utcTimeMillis = utcCalendar.getTimeInMillis();
986270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        convertedLocalTime =
987270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank            CalendarUtilities.getLocalAllDayCalendarTime(utcTimeMillis, localTimeZone);
988270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        // Milliseconds aren't zeroed out and may not be the same
989270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank        assertEquals(convertedLocalTime/1000, correctLocalTime/1000);
990270a17e49669e0bfc7bd2a6303a684a7acd1266dMarc Blank    }
991393208ab154d18a876842777781ab153d34a0281Marc Blank
992393208ab154d18a876842777781ab153d34a0281Marc Blank    public void testGetIntegerValueAsBoolean() {
993393208ab154d18a876842777781ab153d34a0281Marc Blank        ContentValues cv = new ContentValues();
994393208ab154d18a876842777781ab153d34a0281Marc Blank        cv.put("A", 1);
995393208ab154d18a876842777781ab153d34a0281Marc Blank        cv.put("B", 69);
996393208ab154d18a876842777781ab153d34a0281Marc Blank        cv.put("C", 0);
997393208ab154d18a876842777781ab153d34a0281Marc Blank        assertTrue(CalendarUtilities.getIntegerValueAsBoolean(cv, "A"));
998393208ab154d18a876842777781ab153d34a0281Marc Blank        assertTrue(CalendarUtilities.getIntegerValueAsBoolean(cv, "B"));
999393208ab154d18a876842777781ab153d34a0281Marc Blank        assertFalse(CalendarUtilities.getIntegerValueAsBoolean(cv, "C"));
1000393208ab154d18a876842777781ab153d34a0281Marc Blank        assertFalse(CalendarUtilities.getIntegerValueAsBoolean(cv, "D"));
1001393208ab154d18a876842777781ab153d34a0281Marc Blank    }
10025862a85e17e81866ca82a9905577931947fbd44eMarc Blank}
1003455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank
1004455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // TODO Planned unit tests
1005455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // findNextTransition
1006455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // recurrenceFromRrule
1007455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // timeZoneToTziStringImpl
1008455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // getDSTCalendars
1009455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // millisToVCalendarTime
1010455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // millisToEasDateTime
1011455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // getTrueTransitionMinute
1012455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank    // getTrueTransitionHour
1013455f8969854ec1f0ba163ae70cb02e10fb515564Marc Blank
1014