Home | History | Annotate | Download | only in adapter
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.exchange.adapter;
     18 
     19 import android.content.ContentUris;
     20 import android.content.ContentValues;
     21 import android.test.suitebuilder.annotation.SmallTest;
     22 
     23 import com.android.emailcommon.provider.Account;
     24 import com.android.emailcommon.provider.EmailContent;
     25 import com.android.emailcommon.provider.EmailContent.Body;
     26 import com.android.emailcommon.provider.EmailContent.Message;
     27 import com.android.emailcommon.provider.EmailContent.MessageColumns;
     28 import com.android.emailcommon.provider.EmailContent.SyncColumns;
     29 import com.android.emailcommon.provider.Mailbox;
     30 import com.android.exchange.EasSyncService;
     31 import com.android.exchange.adapter.EmailSyncAdapter.EasEmailSyncParser;
     32 import com.android.exchange.adapter.EmailSyncAdapter.EasEmailSyncParser.ServerChange;
     33 import com.android.exchange.provider.EmailContentSetupUtils;
     34 
     35 import java.io.ByteArrayInputStream;
     36 import java.io.IOException;
     37 import java.util.ArrayList;
     38 import java.util.GregorianCalendar;
     39 import java.util.TimeZone;
     40 
     41 @SmallTest
     42 public class EmailSyncAdapterTests extends SyncAdapterTestCase<EmailSyncAdapter> {
     43 
     44     private static final String WHERE_ACCOUNT_KEY = Message.ACCOUNT_KEY + "=?";
     45     private static final String[] ACCOUNT_ARGUMENT = new String[1];
     46 
     47     // A server id that is guaranteed to be test-related
     48     private static final String TEST_SERVER_ID = "__1:22";
     49 
     50     public EmailSyncAdapterTests() {
     51         super();
     52     }
     53 
     54     /**
     55      * Check functionality for getting mime type from a file name (using its extension)
     56      * The default for all unknown files is application/octet-stream
     57      */
     58     public void testGetMimeTypeFromFileName() throws IOException {
     59         EasSyncService service = getTestService();
     60         EmailSyncAdapter adapter = new EmailSyncAdapter(service);
     61         EasEmailSyncParser p = new EasEmailSyncParser(getTestInputStream(), adapter);
     62         // Test a few known types
     63         String mimeType = p.getMimeTypeFromFileName("foo.jpg");
     64         assertEquals("image/jpeg", mimeType);
     65         // Make sure this is case insensitive
     66         mimeType = p.getMimeTypeFromFileName("foo.JPG");
     67         assertEquals("image/jpeg", mimeType);
     68         mimeType = p.getMimeTypeFromFileName("this_is_a_weird_filename.gif");
     69         assertEquals("image/gif", mimeType);
     70         // Test an illegal file name ending with the extension prefix
     71         mimeType = p.getMimeTypeFromFileName("foo.");
     72         assertEquals("application/octet-stream", mimeType);
     73         // Test a really awful name
     74         mimeType = p.getMimeTypeFromFileName(".....");
     75         assertEquals("application/octet-stream", mimeType);
     76         // Test a bare file name (no extension)
     77         mimeType = p.getMimeTypeFromFileName("foo");
     78         assertEquals("application/octet-stream", mimeType);
     79         // And no name at all (null isn't a valid input)
     80         mimeType = p.getMimeTypeFromFileName("");
     81         assertEquals("application/octet-stream", mimeType);
     82     }
     83 
     84     public void testFormatDateTime() throws IOException {
     85         EmailSyncAdapter adapter = getTestSyncAdapter(EmailSyncAdapter.class);
     86         GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
     87         // Calendar is odd, months are zero based, so the first 11 below is December...
     88         calendar.set(2008, 11, 11, 18, 19, 20);
     89         String date = adapter.formatDateTime(calendar);
     90         assertEquals("2008-12-11T18:19:20.000Z", date);
     91         calendar.clear();
     92         calendar.set(2012, 0, 2, 23, 0, 1);
     93         date = adapter.formatDateTime(calendar);
     94         assertEquals("2012-01-02T23:00:01.000Z", date);
     95     }
     96 
     97     public void brokentestSendDeletedItems() throws IOException {
     98         setupAccountMailboxAndMessages(0);
     99         // Setup our adapter and parser
    100         setupSyncParserAndAdapter(mAccount, mMailbox);
    101 
    102         Serializer s = new Serializer();
    103         ArrayList<Long> ids = new ArrayList<Long>();
    104         ArrayList<Long> deletedIds = new ArrayList<Long>();
    105 
    106         // Create account and two mailboxes
    107         mSyncAdapter.mAccount = mAccount;
    108         Mailbox box1 = EmailContentSetupUtils.setupMailbox("box1", mAccount.mId, true,
    109                 mProviderContext);
    110         mSyncAdapter.mMailbox = box1;
    111 
    112         // Create 3 messages
    113         Message msg1 = EmailContentSetupUtils.setupMessage("message1", mAccount.mId, box1.mId,
    114                 true, true, mProviderContext);
    115         ids.add(msg1.mId);
    116         Message msg2 = EmailContentSetupUtils.setupMessage("message2", mAccount.mId, box1.mId,
    117                 true, true, mProviderContext);
    118         ids.add(msg2.mId);
    119         Message msg3 = EmailContentSetupUtils.setupMessage("message3", mAccount.mId, box1.mId,
    120                 true, true, mProviderContext);
    121         ids.add(msg3.mId);
    122         assertEquals(3, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
    123                 getAccountArgument(mAccount.mId)));
    124 
    125         // Delete them
    126         for (long id: ids) {
    127             mResolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, id),
    128                     null, null);
    129         }
    130 
    131         // Confirm that the messages are in the proper table
    132         assertEquals(0, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
    133                 getAccountArgument(mAccount.mId)));
    134         assertEquals(3, EmailContent.count(mProviderContext, Message.DELETED_CONTENT_URI,
    135                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
    136 
    137         // Call code to send deletions; the id's of the ones actually deleted will be in the
    138         // deletedIds list
    139         mSyncAdapter.sendDeletedItems(s, deletedIds, true);
    140         assertEquals(3, deletedIds.size());
    141 
    142         // Clear this out for the next test
    143         deletedIds.clear();
    144 
    145         // Create a new message
    146         Message msg4 = EmailContentSetupUtils.setupMessage("message4", mAccount.mId, box1.mId,
    147                 true, true, mProviderContext);
    148         assertEquals(1, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
    149                 getAccountArgument(mAccount.mId)));
    150         // Find the body for this message
    151         Body body = Body.restoreBodyWithMessageId(mProviderContext, msg4.mId);
    152         // Set its source message to msg2's id
    153         ContentValues values = new ContentValues();
    154         values.put(Body.SOURCE_MESSAGE_KEY, msg2.mId);
    155         body.update(mProviderContext, values);
    156 
    157         // Now send deletions again; this time only two should get deleted; msg2 should NOT be
    158         // deleted as it's referenced by msg4
    159         mSyncAdapter.sendDeletedItems(s, deletedIds, true);
    160         assertEquals(2, deletedIds.size());
    161         assertFalse(deletedIds.contains(msg2.mId));
    162     }
    163 
    164     private String[] getAccountArgument(long id) {
    165         ACCOUNT_ARGUMENT[0] = Long.toString(id);
    166         return ACCOUNT_ARGUMENT;
    167     }
    168 
    169     void setupSyncParserAndAdapter(Account account, Mailbox mailbox) throws IOException {
    170         EasSyncService service = getTestService(account, mailbox);
    171         mSyncAdapter = new EmailSyncAdapter(service);
    172         mSyncParser = new EasEmailSyncParser(getTestInputStream(), mSyncAdapter);
    173     }
    174 
    175     ArrayList<Long> setupAccountMailboxAndMessages(int numMessages) {
    176         ArrayList<Long> ids = new ArrayList<Long>();
    177 
    178         // Create account and two mailboxes
    179         mAccount = EmailContentSetupUtils.setupAccount("account", true, mProviderContext);
    180         mMailbox = EmailContentSetupUtils.setupMailbox("box1", mAccount.mId, true,
    181                 mProviderContext);
    182 
    183         for (int i = 0; i < numMessages; i++) {
    184             Message msg = EmailContentSetupUtils.setupMessage("message" + i, mAccount.mId,
    185                     mMailbox.mId, true, true, mProviderContext);
    186             ids.add(msg.mId);
    187         }
    188 
    189         assertEquals(numMessages, EmailContent.count(mProviderContext, Message.CONTENT_URI,
    190                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
    191         return ids;
    192     }
    193 
    194     public void brokentestDeleteParser() throws IOException {
    195         // Setup some messages
    196         ArrayList<Long> messageIds = setupAccountMailboxAndMessages(3);
    197         ContentValues cv = new ContentValues();
    198         cv.put(SyncColumns.SERVER_ID, TEST_SERVER_ID);
    199         long deleteMessageId = messageIds.get(1);
    200         mResolver.update(ContentUris.withAppendedId(Message.CONTENT_URI, deleteMessageId), cv,
    201                 null, null);
    202 
    203         // Setup our adapter and parser
    204         setupSyncParserAndAdapter(mAccount, mMailbox);
    205 
    206         // Set up an input stream with a delete command
    207         Serializer s = new Serializer(false);
    208         s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, TEST_SERVER_ID).end().done();
    209         byte[] bytes = s.toByteArray();
    210         mSyncParser.resetInput(new ByteArrayInputStream(bytes));
    211         mSyncParser.nextTag(0);
    212 
    213         // Run the delete parser
    214         ArrayList<Long> deleteList = new ArrayList<Long>();
    215         mSyncParser.deleteParser(deleteList, Tags.SYNC_DELETE);
    216         // It should have found the message
    217         assertEquals(1, deleteList.size());
    218         long id = deleteList.get(0);
    219         // And the id's should match
    220         assertEquals(deleteMessageId, id);
    221     }
    222 
    223     public void brokentestChangeParser() throws IOException {
    224         // Setup some messages
    225         ArrayList<Long> messageIds = setupAccountMailboxAndMessages(3);
    226         ContentValues cv = new ContentValues();
    227         int randomFlags = Message.FLAG_INCOMING_MEETING_CANCEL | Message.FLAG_TYPE_FORWARD;
    228         cv.put(SyncColumns.SERVER_ID, TEST_SERVER_ID);
    229         cv.put(MessageColumns.FLAGS, randomFlags);
    230         long changeMessageId = messageIds.get(1);
    231         mResolver.update(ContentUris.withAppendedId(Message.CONTENT_URI, changeMessageId), cv,
    232                 null, null);
    233 
    234         // Setup our adapter and parser
    235         setupSyncParserAndAdapter(mAccount, mMailbox);
    236 
    237         // Set up an input stream with a change command (marking TEST_SERVER_ID unread)
    238         // Note that the test message creation code sets read to "true"
    239         Serializer s = new Serializer(false);
    240         s.start(Tags.SYNC_CHANGE).data(Tags.SYNC_SERVER_ID, TEST_SERVER_ID);
    241         s.start(Tags.SYNC_APPLICATION_DATA);
    242         s.data(Tags.EMAIL_READ, "0");
    243         s.data(Tags.EMAIL2_LAST_VERB_EXECUTED,
    244                 Integer.toString(EmailSyncAdapter.LAST_VERB_FORWARD));
    245         s.end().end().done();
    246         byte[] bytes = s.toByteArray();
    247         mSyncParser.resetInput(new ByteArrayInputStream(bytes));
    248         mSyncParser.nextTag(0);
    249 
    250         // Run the delete parser
    251         ArrayList<ServerChange> changeList = new ArrayList<ServerChange>();
    252         mSyncParser.changeParser(changeList);
    253         // It should have found the message
    254         assertEquals(1, changeList.size());
    255         // And the id's should match
    256         ServerChange change = changeList.get(0);
    257         assertEquals(changeMessageId, change.id);
    258         assertNotNull(change.read);
    259         assertFalse(change.read);
    260         // Make sure we see the forwarded flag AND that the original flags are preserved
    261         assertEquals((Integer)(randomFlags | Message.FLAG_FORWARDED), change.flags);
    262     }
    263 
    264     public void brokentestCleanup() throws IOException {
    265         // Setup some messages
    266         ArrayList<Long> messageIds = setupAccountMailboxAndMessages(3);
    267         // Setup our adapter and parser
    268         setupSyncParserAndAdapter(mAccount, mMailbox);
    269 
    270         // Delete two of the messages, change one
    271         long id = messageIds.get(0);
    272         mResolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, id),
    273                 null, null);
    274         mSyncAdapter.mDeletedIdList.add(id);
    275         id = messageIds.get(1);
    276         mResolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI,
    277                 id), null, null);
    278         mSyncAdapter.mDeletedIdList.add(id);
    279         id = messageIds.get(2);
    280         ContentValues cv = new ContentValues();
    281         cv.put(Message.FLAG_READ, 0);
    282         mResolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI,
    283                 id), cv, null, null);
    284         mSyncAdapter.mUpdatedIdList.add(id);
    285 
    286         // The changed message should still exist
    287         assertEquals(1, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
    288                 getAccountArgument(mAccount.mId)));
    289 
    290         // As well, the two deletions and one update
    291         assertEquals(2, EmailContent.count(mProviderContext, Message.DELETED_CONTENT_URI,
    292                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
    293         assertEquals(1, EmailContent.count(mProviderContext, Message.UPDATED_CONTENT_URI,
    294                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
    295 
    296         // Cleanup (i.e. after sync); should remove items from delete/update tables
    297         mSyncAdapter.cleanup();
    298 
    299         // The three should be gone
    300         assertEquals(0, EmailContent.count(mProviderContext, Message.DELETED_CONTENT_URI,
    301                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
    302         assertEquals(0, EmailContent.count(mProviderContext, Message.UPDATED_CONTENT_URI,
    303                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
    304     }
    305 }
    306