Home | History | Annotate | Download | only in email
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.email;
     18 
     19 import com.android.email.mail.Address;
     20 import com.android.email.mail.Body;
     21 import com.android.email.mail.BodyPart;
     22 import com.android.email.mail.Flag;
     23 import com.android.email.mail.Folder;
     24 import com.android.email.mail.Message;
     25 import com.android.email.mail.MessageTestUtils;
     26 import com.android.email.mail.MessagingException;
     27 import com.android.email.mail.Part;
     28 import com.android.email.mail.Folder.OpenMode;
     29 import com.android.email.mail.Message.RecipientType;
     30 import com.android.email.mail.MessageTestUtils.MessageBuilder;
     31 import com.android.email.mail.MessageTestUtils.MultipartBuilder;
     32 import com.android.email.mail.internet.MimeBodyPart;
     33 import com.android.email.mail.internet.MimeHeader;
     34 import com.android.email.mail.internet.MimeMessage;
     35 import com.android.email.mail.internet.MimeUtility;
     36 import com.android.email.mail.internet.TextBody;
     37 import com.android.email.mail.store.LocalStore;
     38 import com.android.email.mail.store.LocalStoreUnitTests;
     39 import com.android.email.provider.EmailContent;
     40 import com.android.email.provider.EmailProvider;
     41 import com.android.email.provider.ProviderTestUtils;
     42 import com.android.email.provider.EmailContent.Attachment;
     43 import com.android.email.provider.EmailContent.Mailbox;
     44 
     45 import android.content.ContentUris;
     46 import android.content.Context;
     47 import android.database.Cursor;
     48 import android.net.Uri;
     49 import android.test.ProviderTestCase2;
     50 
     51 import java.io.IOException;
     52 import java.util.ArrayList;
     53 import java.util.Date;
     54 
     55 /**
     56  * Tests of the Legacy Conversions code (used by MessagingController).
     57  *
     58  * NOTE:  It would probably make sense to rewrite this using a MockProvider, instead of the
     59  * ProviderTestCase (which is a real provider running on a temp database).  This would be more of
     60  * a true "unit test".
     61  *
     62  * You can run this entire test case with:
     63  *   runtest -c com.android.email.LegacyConversionsTests email
     64  */
     65 public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> {
     66 
     67     private static final String UID = "UID.12345678";
     68     private static final String SENDER = "sender (at) android.com";
     69     private static final String RECIPIENT_TO = "recipient-to (at) android.com";
     70     private static final String RECIPIENT_CC = "recipient-cc (at) android.com";
     71     private static final String RECIPIENT_BCC = "recipient-bcc (at) android.com";
     72     private static final String REPLY_TO = "reply-to (at) android.com";
     73     private static final String SUBJECT = "This is the subject";
     74     private static final String MESSAGE_ID = "Test-Message-ID";
     75     private static final String MESSAGE_ID_2 = "Test-Message-ID-Second";
     76 
     77     EmailProvider mProvider;
     78     Context mProviderContext;
     79     Context mContext;
     80     Account mLegacyAccount = null;
     81     Preferences mPreferences = null;
     82 
     83     public LegacyConversionsTests() {
     84         super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY);
     85     }
     86 
     87     @Override
     88     public void setUp() throws Exception {
     89         super.setUp();
     90         mProviderContext = getMockContext();
     91         mContext = getContext();
     92     }
     93 
     94     @Override
     95     public void tearDown() throws Exception {
     96         super.tearDown();
     97         if (mLegacyAccount != null) {
     98             mLegacyAccount.delete(mPreferences);
     99         }
    100     }
    101 
    102     /**
    103      * TODO: basic Legacy -> Provider Message conversions
    104      * TODO: basic Legacy -> Provider Body conversions
    105      * TODO: rainy day tests of all kinds
    106      */
    107 
    108     /**
    109      * Test basic conversion from Store message to Provider message
    110      *
    111      * TODO: Not a complete test of all fields, and some fields need special tests (e.g. flags)
    112      * TODO: There are many special cases in the tested function, that need to be
    113      * tested here as well.
    114      */
    115     public void testUpdateMessageFields() throws MessagingException {
    116         MimeMessage message = buildTestMessage(RECIPIENT_TO, RECIPIENT_CC, RECIPIENT_BCC,
    117                 REPLY_TO, SENDER, SUBJECT, null);
    118         EmailContent.Message localMessage = new EmailContent.Message();
    119 
    120         boolean result = LegacyConversions.updateMessageFields(localMessage, message, 1, 1);
    121         assertTrue(result);
    122         checkProviderMessage("testUpdateMessageFields", message, localMessage);
    123     }
    124 
    125     /**
    126      * Test basic conversion from Store message to Provider message, when the provider message
    127      * does not have a proper message-id.
    128      */
    129     public void testUpdateMessageFieldsNoMessageId() throws MessagingException {
    130         MimeMessage message = buildTestMessage(RECIPIENT_TO, RECIPIENT_CC, RECIPIENT_BCC,
    131                 REPLY_TO, SENDER, SUBJECT, null);
    132         EmailContent.Message localMessage = new EmailContent.Message();
    133 
    134         // If the source message-id is null, the target should be left as-is
    135         localMessage.mMessageId = MESSAGE_ID_2;
    136         message.removeHeader("Message-ID");
    137 
    138         boolean result = LegacyConversions.updateMessageFields(localMessage, message, 1, 1);
    139         assertTrue(result);
    140         assertEquals(MESSAGE_ID_2, localMessage.mMessageId);
    141     }
    142 
    143     /**
    144      * Build a lightweight Store message with simple field population
    145      */
    146     private MimeMessage buildTestMessage(String to, String cc, String bcc, String replyTo,
    147             String sender, String subject, String content) throws MessagingException {
    148         MimeMessage message = new MimeMessage();
    149 
    150         if (to != null) {
    151             Address[] addresses = Address.parse(to);
    152             message.setRecipients(RecipientType.TO, addresses);
    153         }
    154         if (cc != null) {
    155             Address[] addresses = Address.parse(cc);
    156             message.setRecipients(RecipientType.CC, addresses);
    157         }
    158         if (bcc != null) {
    159             Address[] addresses = Address.parse(bcc);
    160             message.setRecipients(RecipientType.BCC, addresses);
    161         }
    162         if (replyTo != null) {
    163             Address[] addresses = Address.parse(replyTo);
    164             message.setReplyTo(addresses);
    165         }
    166         if (sender != null) {
    167             Address[] addresses = Address.parse(sender);
    168             message.setFrom(addresses[0]);
    169         }
    170         if (subject != null) {
    171             message.setSubject(subject);
    172         }
    173         if (content != null) {
    174             TextBody body = new TextBody(content);
    175             message.setBody(body);
    176         }
    177 
    178         message.setUid(UID);
    179         message.setSentDate(new Date());
    180         message.setInternalDate(new Date());
    181         message.setMessageId(MESSAGE_ID);
    182         return message;
    183     }
    184 
    185     /**
    186      * Basic test of body parts conversion from Store message to Provider message.
    187      * This tests that a null body part simply results in null text, and does not crash
    188      * or return "null".
    189      *
    190      * TODO very incomplete, there are many permutations to be explored
    191      */
    192     public void testUpdateBodyFieldsNullText() throws MessagingException {
    193         EmailContent.Body localBody = new EmailContent.Body();
    194         EmailContent.Message localMessage = new EmailContent.Message();
    195         ArrayList<Part> viewables = new ArrayList<Part>();
    196         Part emptyTextPart = new MimeBodyPart(null, "text/plain");
    197         viewables.add(emptyTextPart);
    198 
    199         // a "null" body part of type text/plain should result in a null mTextContent
    200         boolean result = LegacyConversions.updateBodyFields(localBody, localMessage, viewables);
    201         assertTrue(result);
    202         assertNull(localBody.mTextContent);
    203     }
    204 
    205     /**
    206      * Sunny day test of adding attachments from an IMAP/POP message.
    207      */
    208     public void testAddAttachments() throws MessagingException, IOException {
    209         // Prepare a local message to add the attachments to
    210         final long accountId = 1;
    211         final long mailboxId = 1;
    212 
    213         // test 1: legacy message using content-type:name style for name
    214         final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
    215                 "local-message", accountId, mailboxId, false, true, mProviderContext);
    216         final Message legacyMessage = prepareLegacyMessageWithAttachments(2, false, false);
    217         convertAndCheckcheckAddedAttachments(localMessage, legacyMessage);
    218 
    219         // test 2: legacy message using content-disposition:filename style for name
    220         final EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage(
    221                 "local-message", accountId, mailboxId, false, true, mProviderContext);
    222         final Message legacyMessage2 = prepareLegacyMessageWithAttachments(2, false, true);
    223         convertAndCheckcheckAddedAttachments(localMessage2, legacyMessage2);
    224     }
    225 
    226     /**
    227      * Helper for testAddAttachments
    228      */
    229     private void convertAndCheckcheckAddedAttachments(final EmailContent.Message localMessage,
    230             final Message legacyMessage) throws MessagingException, IOException {
    231         // Now, convert from legacy to provider and see what happens
    232         ArrayList<Part> viewables = new ArrayList<Part>();
    233         ArrayList<Part> attachments = new ArrayList<Part>();
    234         MimeUtility.collectParts(legacyMessage, viewables, attachments);
    235         LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
    236 
    237         // Read back all attachments for message and check field values
    238         Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
    239         Cursor c = mProviderContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION,
    240                 null, null, null);
    241         try {
    242             assertEquals(2, c.getCount());
    243             while (c.moveToNext()) {
    244                 Attachment attachment = Attachment.getContent(c, Attachment.class);
    245                 if ("101".equals(attachment.mLocation)) {
    246                     checkAttachment("attachment1Part", attachments.get(0), attachment);
    247                 } else if ("102".equals(attachment.mLocation)) {
    248                     checkAttachment("attachment2Part", attachments.get(1), attachment);
    249                 } else {
    250                     fail("Unexpected attachment with location " + attachment.mLocation);
    251                 }
    252             }
    253         } finally {
    254             c.close();
    255         }
    256     }
    257 
    258     /**
    259      * Test that attachments aren't re-added in the DB.  This supports the "partial download"
    260      * nature of POP messages.
    261      */
    262     public void testAddDuplicateAttachments() throws MessagingException, IOException {
    263         // Prepare a local message to add the attachments to
    264         final long accountId = 1;
    265         final long mailboxId = 1;
    266         final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
    267                 "local-message", accountId, mailboxId, false, true, mProviderContext);
    268 
    269         // Prepare a legacy message with attachments
    270         Message legacyMessage = prepareLegacyMessageWithAttachments(2, false, false);
    271 
    272         // Now, convert from legacy to provider and see what happens
    273         ArrayList<Part> viewables = new ArrayList<Part>();
    274         ArrayList<Part> attachments = new ArrayList<Part>();
    275         MimeUtility.collectParts(legacyMessage, viewables, attachments);
    276         LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
    277 
    278         // Confirm two attachment objects created
    279         Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
    280         assertEquals(2, EmailContent.count(mProviderContext, uri, null, null));
    281 
    282         // Now add the attachments again and confirm there are still only two
    283         LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
    284         assertEquals(2, EmailContent.count(mProviderContext, uri, null, null));
    285 
    286         // Now add a 3rd & 4th attachment and make sure the total is 4, not 2 or 6
    287         legacyMessage = prepareLegacyMessageWithAttachments(4, false, false);
    288         viewables = new ArrayList<Part>();
    289         attachments = new ArrayList<Part>();
    290         MimeUtility.collectParts(legacyMessage, viewables, attachments);
    291         LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, false);
    292         assertEquals(4, EmailContent.count(mProviderContext, uri, null, null));
    293     }
    294 
    295     /**
    296      * Sunny day test of adding attachments in "local account upgrade" mode
    297      */
    298     public void testLocalUpgradeAttachments() throws MessagingException, IOException {
    299         // Prepare a local message to add the attachments to
    300         final long accountId = 1;
    301         final long mailboxId = 1;
    302         final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
    303                 "local-upgrade", accountId, mailboxId, false, true, mProviderContext);
    304 
    305         // Prepare a legacy message with attachments
    306         final Message legacyMessage = prepareLegacyMessageWithAttachments(2, true, false);
    307 
    308         // Now, convert from legacy to provider and see what happens
    309         ArrayList<Part> viewables = new ArrayList<Part>();
    310         ArrayList<Part> attachments = new ArrayList<Part>();
    311         MimeUtility.collectParts(legacyMessage, viewables, attachments);
    312         LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments, true);
    313 
    314         // Read back all attachments for message and check field values
    315         Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
    316         Cursor c = mProviderContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION,
    317                 null, null, null);
    318         try {
    319             assertEquals(2, c.getCount());
    320             while (c.moveToNext()) {
    321                 Attachment attachment = Attachment.getContent(c, Attachment.class);
    322                 // This attachment should look as if created by modern (provider) MessageCompose.
    323                 // 1. find the original that it was created from
    324                 Part fromPart = null;
    325                 for (Part from : attachments) {
    326                     String contentType = MimeUtility.unfoldAndDecode(from.getContentType());
    327                     String name = MimeUtility.getHeaderParameter(contentType, "name");
    328                     if (name.equals(attachment.mFileName)) {
    329                         fromPart = from;
    330                         break;
    331                     }
    332                 }
    333                 assertTrue(fromPart != null);
    334                 // 2. Check values
    335                 checkAttachment(attachment.mFileName, fromPart, attachment);
    336             }
    337         } finally {
    338             c.close();
    339         }
    340     }
    341 
    342     /**
    343      * Prepare a legacy message with 1+ attachments
    344      * @param numAttachments how many attachments to add
    345      * @param localData if true, attachments are "local" data.  false = "remote" (from server)
    346      * @param filenameInDisposition False: attachment names are sent as content-type:name.  True:
    347      *          attachment names are sent as content-disposition:filename.
    348      */
    349     private Message prepareLegacyMessageWithAttachments(int numAttachments, boolean localData,
    350             boolean filenameInDisposition) throws MessagingException {
    351         // First, build one or more attachment parts
    352         MultipartBuilder mpBuilder = new MultipartBuilder("multipart/mixed");
    353         for (int i = 1; i <= numAttachments; ++i) {
    354             // construct parameter parts for content-type:name or content-disposition:filename.
    355             String name = "";
    356             String filename = "";
    357             String quotedName = "\"test-attachment-" + i + "\"";
    358             if (filenameInDisposition) {
    359                 filename = ";\n filename=" + quotedName;
    360             } else {
    361                 name = ";\n name=" + quotedName;
    362             }
    363             if (localData) {
    364                 // generate an attachment that was generated by legacy code (e.g. donut)
    365                 // for test of upgrading accounts in place
    366                 // This creator models the code in legacy MessageCompose
    367                 Uri uri = Uri.parse("content://test/attachment/" + i);
    368                 MimeBodyPart bp = new MimeBodyPart(
    369                         new LocalStore.LocalAttachmentBody(uri, mProviderContext));
    370                 bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg" + name);
    371                 bp.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
    372                 bp.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, "attachment" + filename);
    373                 mpBuilder.addBodyPart(bp);
    374             } else {
    375                 // generate an attachment that came from a server
    376                 BodyPart attachmentPart = MessageTestUtils.bodyPart("image/jpg", null);
    377 
    378                 // name=attachmentN size=N00 location=10N
    379                 attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg" + name);
    380                 attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
    381                 attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
    382                         "attachment" + filename +  ";\n size=" + i + "00");
    383                 attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "10" + i);
    384 
    385                 mpBuilder.addBodyPart(attachmentPart);
    386             }
    387         }
    388 
    389         // Now build a message with them
    390         final Message legacyMessage = new MessageBuilder()
    391             .setBody(new MultipartBuilder("multipart/mixed")
    392                      .addBodyPart(MessageTestUtils.bodyPart("text/html", null))
    393                      .addBodyPart(mpBuilder.buildBodyPart())
    394                      .build())
    395                 .build();
    396 
    397         return legacyMessage;
    398     }
    399 
    400     /**
    401      * Test the stringInequal helper
    402      */
    403     public void testStringInequal() {
    404         // Pairs that are "equal"
    405         assertFalse(LegacyConversions.stringNotEqual(null, null));
    406         assertFalse(LegacyConversions.stringNotEqual(null, ""));
    407         assertFalse(LegacyConversions.stringNotEqual("", null));
    408         assertFalse(LegacyConversions.stringNotEqual("", ""));
    409         assertFalse(LegacyConversions.stringNotEqual("string-equal", "string-equal"));
    410         // Pairs that are "inequal"
    411         assertTrue(LegacyConversions.stringNotEqual(null, "string-inequal"));
    412         assertTrue(LegacyConversions.stringNotEqual("", "string-inequal"));
    413         assertTrue(LegacyConversions.stringNotEqual("string-inequal", null));
    414         assertTrue(LegacyConversions.stringNotEqual("string-inequal", ""));
    415         assertTrue(LegacyConversions.stringNotEqual("string-inequal-a", "string-inequal-b"));
    416     }
    417 
    418     /**
    419      * Compare attachment that was converted from Part (expected) to Provider Attachment (actual)
    420      *
    421      * TODO content URI should only be set if we also saved a file
    422      * TODO other data encodings
    423      */
    424     private void checkAttachment(String tag, Part expected, EmailContent.Attachment actual)
    425             throws MessagingException {
    426         String contentType = MimeUtility.unfoldAndDecode(expected.getContentType());
    427         String contentTypeName = MimeUtility.getHeaderParameter(contentType, "name");
    428         assertEquals(tag, expected.getMimeType(), actual.mMimeType);
    429         String disposition = expected.getDisposition();
    430         String sizeString = MimeUtility.getHeaderParameter(disposition, "size");
    431         String dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename");
    432         long expectedSize = (sizeString != null) ? Long.parseLong(sizeString) : 0;
    433         assertEquals(tag, expectedSize, actual.mSize);
    434         assertEquals(tag, expected.getContentId(), actual.mContentId);
    435 
    436         // filename is either content-type:name or content-disposition:filename
    437         String expectedName = (contentTypeName != null) ? contentTypeName : dispositionFilename;
    438         assertEquals(tag, expectedName, actual.mFileName);
    439 
    440         // content URI either both null or both matching
    441         String expectedUriString = null;
    442         Body body = expected.getBody();
    443         if (body instanceof LocalStore.LocalAttachmentBody) {
    444             LocalStore.LocalAttachmentBody localBody = (LocalStore.LocalAttachmentBody) body;
    445             Uri contentUri = localBody.getContentUri();
    446             if (contentUri != null) {
    447                 expectedUriString = contentUri.toString();
    448             }
    449         }
    450         assertEquals(tag, expectedUriString, actual.mContentUri);
    451 
    452         assertTrue(tag, 0 != actual.mMessageKey);
    453 
    454         // location is either both null or both matching
    455         String expectedPartId = null;
    456         String[] storeData = expected.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
    457         if (storeData != null && storeData.length > 0) {
    458             expectedPartId = storeData[0];
    459         }
    460         assertEquals(tag, expectedPartId, actual.mLocation);
    461         assertEquals(tag, "B", actual.mEncoding);
    462     }
    463 
    464     /**
    465      * TODO: Sunny day test of adding attachments from a POP message.
    466      */
    467 
    468     /**
    469      * Sunny day tests of converting an original message to a legacy message
    470      */
    471     public void testMakeLegacyMessage() throws MessagingException {
    472         // Set up and store a message in the provider
    473         long account1Id = 1;
    474         long mailbox1Id = 1;
    475 
    476         // Test message 1: No body
    477         EmailContent.Message localMessage1 = ProviderTestUtils.setupMessage("make-legacy",
    478                 account1Id, mailbox1Id, false, true, mProviderContext);
    479         Message getMessage1 = LegacyConversions.makeMessage(mProviderContext, localMessage1);
    480         checkLegacyMessage("no body", localMessage1, getMessage1);
    481 
    482         // Test message 2: Simple body
    483         EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage("make-legacy",
    484                 account1Id, mailbox1Id, true, false, mProviderContext);
    485         localMessage2.mTextReply = null;
    486         localMessage2.mHtmlReply = null;
    487         localMessage2.mIntroText = null;
    488         localMessage2.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
    489         localMessage2.save(mProviderContext);
    490         Message getMessage2 = LegacyConversions.makeMessage(mProviderContext, localMessage2);
    491         checkLegacyMessage("simple body", localMessage2, getMessage2);
    492 
    493         // Test message 3: Body + replied-to text
    494         EmailContent.Message localMessage3 = ProviderTestUtils.setupMessage("make-legacy",
    495                 account1Id, mailbox1Id, true, false, mProviderContext);
    496         localMessage3.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
    497         localMessage3.mFlags |= EmailContent.Message.FLAG_TYPE_REPLY;
    498         localMessage3.save(mProviderContext);
    499         Message getMessage3 = LegacyConversions.makeMessage(mProviderContext, localMessage3);
    500         checkLegacyMessage("reply-to", localMessage3, getMessage3);
    501 
    502         // Test message 4: Body + forwarded text
    503         EmailContent.Message localMessage4 = ProviderTestUtils.setupMessage("make-legacy",
    504                 account1Id, mailbox1Id, true, false, mProviderContext);
    505         localMessage4.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
    506         localMessage4.mFlags |= EmailContent.Message.FLAG_TYPE_FORWARD;
    507         localMessage4.save(mProviderContext);
    508         Message getMessage4 = LegacyConversions.makeMessage(mProviderContext, localMessage4);
    509         checkLegacyMessage("forwarding", localMessage4, getMessage4);
    510     }
    511 
    512     /**
    513      * Check equality of a pair of converted messages
    514      */
    515     private void checkProviderMessage(String tag, Message expect, EmailContent.Message actual)
    516             throws MessagingException {
    517         assertEquals(tag, expect.getUid(), actual.mServerId);
    518         assertEquals(tag, expect.getSubject(), actual.mSubject);
    519         assertEquals(tag, Address.pack(expect.getFrom()), actual.mFrom);
    520         assertEquals(tag, expect.getSentDate().getTime(), actual.mTimeStamp);
    521         assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.TO)), actual.mTo);
    522         assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.CC)), actual.mCc);
    523         assertEquals(tag, ((MimeMessage)expect).getMessageId(), actual.mMessageId);
    524         assertEquals(tag, expect.isSet(Flag.SEEN), actual.mFlagRead);
    525         assertEquals(tag, expect.isSet(Flag.FLAGGED), actual.mFlagFavorite);
    526     }
    527 
    528     /**
    529      * Check equality of a pair of converted messages
    530      */
    531     private void checkLegacyMessage(String tag, EmailContent.Message expect, Message actual)
    532             throws MessagingException {
    533         assertEquals(tag, expect.mServerId, actual.getUid());
    534         assertEquals(tag, expect.mServerTimeStamp, actual.getInternalDate().getTime());
    535         assertEquals(tag, expect.mSubject, actual.getSubject());
    536         assertEquals(tag, expect.mFrom, Address.pack(actual.getFrom()));
    537         assertEquals(tag, expect.mTimeStamp, actual.getSentDate().getTime());
    538         assertEquals(tag, expect.mTo, Address.pack(actual.getRecipients(RecipientType.TO)));
    539         assertEquals(tag, expect.mCc, Address.pack(actual.getRecipients(RecipientType.CC)));
    540         assertEquals(tag, expect.mBcc, Address.pack(actual.getRecipients(RecipientType.BCC)));
    541         assertEquals(tag, expect.mReplyTo, Address.pack(actual.getReplyTo()));
    542         assertEquals(tag, expect.mMessageId, ((MimeMessage)actual).getMessageId());
    543         // check flags
    544         assertEquals(tag, expect.mFlagRead, actual.isSet(Flag.SEEN));
    545         assertEquals(tag, expect.mFlagFavorite, actual.isSet(Flag.FLAGGED));
    546 
    547         // Check the body of the message
    548         ArrayList<Part> viewables = new ArrayList<Part>();
    549         ArrayList<Part> attachments = new ArrayList<Part>();
    550         MimeUtility.collectParts(actual, viewables, attachments);
    551         String get1Text = null;
    552         String get1Html = null;
    553         String get1TextReply = null;
    554         String get1HtmlReply = null;
    555         String get1TextIntro = null;
    556         for (Part viewable : viewables) {
    557             String text = MimeUtility.getTextFromPart(viewable);
    558             boolean isHtml = viewable.getMimeType().equalsIgnoreCase("text/html");
    559             String[] headers = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART);
    560             if (headers != null) {
    561                 String header = headers[0];
    562                 boolean isReply = LegacyConversions.BODY_QUOTED_PART_REPLY.equalsIgnoreCase(header);
    563                 boolean isFwd = LegacyConversions.BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(header);
    564                 boolean isIntro = LegacyConversions.BODY_QUOTED_PART_INTRO.equalsIgnoreCase(header);
    565                 if (isReply || isFwd) {
    566                     if (isHtml) {
    567                         get1HtmlReply = text;
    568                     } else {
    569                         get1TextReply = text;
    570                     }
    571                 } else if (isIntro) {
    572                     get1TextIntro = text;
    573                 }
    574                 // Check flags
    575                 int replyTypeFlags = expect.mFlags & EmailContent.Message.FLAG_TYPE_MASK;
    576                 if (isReply) {
    577                     assertEquals(tag, EmailContent.Message.FLAG_TYPE_REPLY, replyTypeFlags);
    578                 }
    579                 if (isFwd) {
    580                     assertEquals(tag, EmailContent.Message.FLAG_TYPE_FORWARD, replyTypeFlags);
    581                 }
    582             } else {
    583                 if (isHtml) {
    584                     get1Html = text;
    585                 } else {
    586                     get1Text = text;
    587                 }
    588             }
    589         }
    590         assertEquals(tag, expect.mText, get1Text);
    591         assertEquals(tag, expect.mHtml, get1Html);
    592         assertEquals(tag, expect.mTextReply, get1TextReply);
    593         assertEquals(tag, expect.mHtmlReply, get1HtmlReply);
    594         assertEquals(tag, expect.mIntroText, get1TextIntro);
    595 
    596         // TODO Check the attachments
    597 
    598 //      cv.put("attachment_count", attachments.size());
    599     }
    600 
    601     /**
    602      * Test conversion of a legacy account to a provider account
    603      */
    604     public void testMakeProviderAccount() throws MessagingException {
    605 
    606         setupLegacyAccount("testMakeProviderAccount", true);
    607         EmailContent.Account toAccount =
    608             LegacyConversions.makeAccount(mProviderContext, mLegacyAccount);
    609         checkProviderAccount("testMakeProviderAccount", mLegacyAccount, toAccount);
    610     }
    611 
    612     /**
    613      * Test conversion of a provider account to a legacy account
    614      */
    615     public void testMakeLegacyAccount() throws MessagingException {
    616         EmailContent.Account fromAccount = ProviderTestUtils.setupAccount("convert-to-legacy",
    617                 false, mProviderContext);
    618         fromAccount.mHostAuthRecv =
    619             ProviderTestUtils.setupHostAuth("legacy-recv", 0, false, mProviderContext);
    620         fromAccount.mHostAuthSend =
    621             ProviderTestUtils.setupHostAuth("legacy-send", 0, false, mProviderContext);
    622         fromAccount.save(mProviderContext);
    623 
    624         Account toAccount = LegacyConversions.makeLegacyAccount(mProviderContext, fromAccount);
    625         checkLegacyAccount("testMakeLegacyAccount", fromAccount, toAccount);
    626     }
    627 
    628     /**
    629      * Setup a legacy account in mLegacyAccount with many fields prefilled.
    630      */
    631     private void setupLegacyAccount(String name, boolean saveIt) {
    632         // prefs & legacy account are saved for cleanup (it's stored in the real prefs file)
    633         mPreferences = Preferences.getPreferences(mProviderContext);
    634         mLegacyAccount = new Account(mProviderContext);
    635 
    636         // fill in useful fields
    637         mLegacyAccount.mUuid = "test-uid-" + name;
    638         mLegacyAccount.mStoreUri = "store://test/" + name;
    639         mLegacyAccount.mLocalStoreUri = "local://localhost/" + name;
    640         mLegacyAccount.mSenderUri = "sender://test/" + name;
    641         mLegacyAccount.mDescription = "description " + name;
    642         mLegacyAccount.mName = "name " + name;
    643         mLegacyAccount.mEmail = "email " + name;
    644         mLegacyAccount.mAutomaticCheckIntervalMinutes = 100;
    645         mLegacyAccount.mLastAutomaticCheckTime = 200;
    646         mLegacyAccount.mNotifyNewMail = true;
    647         mLegacyAccount.mDraftsFolderName = "drafts " + name;
    648         mLegacyAccount.mSentFolderName = "sent " + name;
    649         mLegacyAccount.mTrashFolderName = "trash " + name;
    650         mLegacyAccount.mOutboxFolderName = "outbox " + name;
    651         mLegacyAccount.mAccountNumber = 300;
    652         mLegacyAccount.mVibrate = true;
    653         mLegacyAccount.mVibrateWhenSilent = false;
    654         mLegacyAccount.mRingtoneUri = "ringtone://test/" + name;
    655         mLegacyAccount.mSyncWindow = 400;
    656         mLegacyAccount.mBackupFlags = 0;
    657         mLegacyAccount.mDeletePolicy = Account.DELETE_POLICY_NEVER;
    658         mLegacyAccount.mSecurityFlags = 500;
    659         mLegacyAccount.mSignature = "signature " + name;
    660 
    661         if (saveIt) {
    662             mLegacyAccount.save(mPreferences);
    663         }
    664     }
    665 
    666     /**
    667      * Compare a provider account to the legacy account it was created from
    668      */
    669     private void checkProviderAccount(String tag, Account expect, EmailContent.Account actual)
    670             throws MessagingException {
    671         assertEquals(tag + " description", expect.getDescription(), actual.mDisplayName);
    672         assertEquals(tag + " email", expect.getEmail(), actual.mEmailAddress);
    673         assertEquals(tag + " sync key", null, actual.mSyncKey);
    674         assertEquals(tag + " lookback", expect.getSyncWindow(), actual.mSyncLookback);
    675         assertEquals(tag + " sync intvl", expect.getAutomaticCheckIntervalMinutes(),
    676                 actual.mSyncInterval);
    677         // These asserts are checking mHostAuthKeyRecv & mHostAuthKeySend
    678         assertEquals(tag + " store", expect.getStoreUri(), actual.getStoreUri(mProviderContext));
    679         assertEquals(tag + " sender", expect.getSenderUri(), actual.getSenderUri(mProviderContext));
    680         // Synthesize & check flags
    681         int expectFlags = 0;
    682         if (expect.mNotifyNewMail) expectFlags |= EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL;
    683         if (expect.mVibrate) expectFlags |= EmailContent.Account.FLAGS_VIBRATE_ALWAYS;
    684         if (expect.mVibrateWhenSilent)
    685             expectFlags |= EmailContent.Account.FLAGS_VIBRATE_WHEN_SILENT;
    686         expectFlags |=
    687             (expect.mDeletePolicy << EmailContent.Account.FLAGS_DELETE_POLICY_SHIFT)
    688                 & EmailContent.Account.FLAGS_DELETE_POLICY_MASK;
    689         assertEquals(tag + " flags", expectFlags, actual.mFlags);
    690         assertEquals(tag + " default", false, actual.mIsDefault);
    691         assertEquals(tag + " uuid", expect.getUuid(), actual.mCompatibilityUuid);
    692         assertEquals(tag + " name", expect.getName(), actual.mSenderName);
    693         assertEquals(tag + " ringtone", expect.getRingtone(), actual.mRingtoneUri);
    694         assertEquals(tag + " proto vers", expect.mProtocolVersion, actual.mProtocolVersion);
    695         assertEquals(tag + " new count", 0, actual.mNewMessageCount);
    696         assertEquals(tag + " security", expect.mSecurityFlags, actual.mSecurityFlags);
    697         assertEquals(tag + " sec sync key", null, actual.mSecuritySyncKey);
    698         assertEquals(tag + " signature", expect.mSignature, actual.mSignature);
    699     }
    700 
    701     /**
    702      * Compare a legacy account to the provider account it was created from
    703      */
    704     private void checkLegacyAccount(String tag, EmailContent.Account expect, Account actual)
    705             throws MessagingException {
    706         int expectFlags = expect.getFlags();
    707 
    708         assertEquals(tag + " uuid", expect.mCompatibilityUuid, actual.mUuid);
    709         assertEquals(tag + " store", expect.getStoreUri(mProviderContext), actual.mStoreUri);
    710         assertTrue(actual.mLocalStoreUri.startsWith("local://localhost"));
    711         assertEquals(tag + " sender", expect.getSenderUri(mProviderContext), actual.mSenderUri);
    712         assertEquals(tag + " description", expect.getDisplayName(), actual.mDescription);
    713         assertEquals(tag + " name", expect.getSenderName(), actual.mName);
    714         assertEquals(tag + " email", expect.getEmailAddress(), actual.mEmail);
    715         assertEquals(tag + " checkintvl", expect.getSyncInterval(),
    716                 actual.mAutomaticCheckIntervalMinutes);
    717         assertEquals(tag + " checktime", 0, actual.mLastAutomaticCheckTime);
    718         assertEquals(tag + " notify",
    719                 (expectFlags & EmailContent.Account.FLAGS_NOTIFY_NEW_MAIL) != 0,
    720                 actual.mNotifyNewMail);
    721         assertEquals(tag + " drafts", null, actual.mDraftsFolderName);
    722         assertEquals(tag + " sent", null, actual.mSentFolderName);
    723         assertEquals(tag + " trash", null, actual.mTrashFolderName);
    724         assertEquals(tag + " outbox", null, actual.mOutboxFolderName);
    725         assertEquals(tag + " acct #", -1, actual.mAccountNumber);
    726         assertEquals(tag + " vibrate",
    727                 (expectFlags & EmailContent.Account.FLAGS_VIBRATE_ALWAYS) != 0,
    728                 actual.mVibrate);
    729         assertEquals(tag + " vibrateSilent",
    730                 (expectFlags & EmailContent.Account.FLAGS_VIBRATE_WHEN_SILENT) != 0,
    731                 actual.mVibrateWhenSilent);
    732         assertEquals(tag + " ", expect.getRingtone(), actual.mRingtoneUri);
    733         assertEquals(tag + " sync window", expect.getSyncLookback(), actual.mSyncWindow);
    734         assertEquals(tag + " backup flags", 0, actual.mBackupFlags);
    735         assertEquals(tag + " proto vers", expect.mProtocolVersion, actual.mProtocolVersion);
    736         assertEquals(tag + " delete policy", expect.getDeletePolicy(), actual.getDeletePolicy());
    737         assertEquals(tag + " security", expect.mSecurityFlags, actual.mSecurityFlags);
    738         assertEquals(tag + " signature", expect.mSignature, actual.mSignature);
    739     }
    740 
    741     /**
    742      * Test conversion of a legacy mailbox to a provider mailbox
    743      */
    744     public void testMakeProviderMailbox() throws MessagingException {
    745         EmailContent.Account toAccount = ProviderTestUtils.setupAccount("convert-mailbox",
    746                 true, mProviderContext);
    747         Folder fromFolder = buildTestFolder("INBOX");
    748         Mailbox toMailbox = LegacyConversions.makeMailbox(mProviderContext, toAccount, fromFolder);
    749 
    750         // Now test fields in created mailbox
    751         assertEquals("INBOX", toMailbox.mDisplayName);
    752         assertNull(toMailbox.mServerId);
    753         assertNull(toMailbox.mParentServerId);
    754         assertEquals(toAccount.mId, toMailbox.mAccountKey);
    755         assertEquals(Mailbox.TYPE_INBOX, toMailbox.mType);
    756         assertEquals(0, toMailbox.mDelimiter);
    757         assertNull(toMailbox.mSyncKey);
    758         assertEquals(0, toMailbox.mSyncLookback);
    759         assertEquals(0, toMailbox.mSyncInterval);
    760         assertEquals(0, toMailbox.mSyncTime);
    761         assertEquals(100, toMailbox.mUnreadCount);
    762         assertTrue(toMailbox.mFlagVisible);
    763         assertEquals(0, toMailbox.mFlags);
    764         assertEquals(Email.VISIBLE_LIMIT_DEFAULT, toMailbox.mVisibleLimit);
    765         assertNull(toMailbox.mSyncStatus);
    766     }
    767 
    768     /**
    769      * Build a lightweight Store Folder with simple field population.  The folder is "open"
    770      * and should be closed by the caller.
    771      */
    772     private Folder buildTestFolder(String folderName) throws MessagingException {
    773         String localStoreUri =
    774             "local://localhost/" + mProviderContext.getDatabasePath(LocalStoreUnitTests.DB_NAME);
    775         LocalStore store = (LocalStore) LocalStore.newInstance(localStoreUri, getContext(), null);
    776         LocalStore.LocalFolder folder = (LocalStore.LocalFolder) store.getFolder(folderName);
    777         folder.open(OpenMode.READ_WRITE, null);     // this will create it
    778 
    779         // set a few fields to test values
    780         // folder.getName - set by getFolder()
    781         folder.setUnreadMessageCount(100);
    782 
    783         return folder;
    784     }
    785 }
    786