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