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