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.EmailProvider;
20import com.android.email.provider.ProviderTestUtils;
21import com.android.emailcommon.internet.MimeBodyPart;
22import com.android.emailcommon.internet.MimeHeader;
23import com.android.emailcommon.internet.MimeMessage;
24import com.android.emailcommon.internet.MimeUtility;
25import com.android.emailcommon.internet.TextBody;
26import com.android.emailcommon.mail.Address;
27import com.android.emailcommon.mail.BodyPart;
28import com.android.emailcommon.mail.Flag;
29import com.android.emailcommon.mail.Message;
30import com.android.emailcommon.mail.Message.RecipientType;
31import com.android.emailcommon.mail.MessageTestUtils;
32import com.android.emailcommon.mail.MessageTestUtils.MessageBuilder;
33import com.android.emailcommon.mail.MessageTestUtils.MultipartBuilder;
34import com.android.emailcommon.mail.MessagingException;
35import com.android.emailcommon.mail.Part;
36import com.android.emailcommon.provider.EmailContent;
37import com.android.emailcommon.provider.EmailContent.Attachment;
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    Preferences mPreferences = null;
76
77    public LegacyConversionsTests() {
78        super(EmailProvider.class, EmailContent.AUTHORITY);
79    }
80
81    @Override
82    public void setUp() throws Exception {
83        super.setUp();
84        mProviderContext = getMockContext();
85        mContext = getContext();
86    }
87
88    /**
89     * TODO: basic Legacy -> Provider Message conversions
90     * TODO: basic Legacy -> Provider Body conversions
91     * TODO: rainy day tests of all kinds
92     */
93
94    /**
95     * Test basic conversion from Store message to Provider message
96     *
97     * TODO: Not a complete test of all fields, and some fields need special tests (e.g. flags)
98     * TODO: There are many special cases in the tested function, that need to be
99     * tested here as well.
100     */
101    public void testUpdateMessageFields() throws MessagingException {
102        MimeMessage message = buildTestMessage(RECIPIENT_TO, RECIPIENT_CC, RECIPIENT_BCC,
103                REPLY_TO, SENDER, SUBJECT, null);
104        EmailContent.Message localMessage = new EmailContent.Message();
105
106        boolean result = LegacyConversions.updateMessageFields(localMessage, message, 1, 1);
107        assertTrue(result);
108        checkProviderMessage("testUpdateMessageFields", message, localMessage);
109    }
110
111    /**
112     * Test basic conversion from Store message to Provider message, when the provider message
113     * does not have a proper message-id.
114     */
115    public void testUpdateMessageFieldsNoMessageId() 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        // If the source message-id is null, the target should be left as-is
121        localMessage.mMessageId = MESSAGE_ID_2;
122        message.removeHeader("Message-ID");
123
124        boolean result = LegacyConversions.updateMessageFields(localMessage, message, 1, 1);
125        assertTrue(result);
126        assertEquals(MESSAGE_ID_2, localMessage.mMessageId);
127    }
128
129    /**
130     * Build a lightweight Store message with simple field population
131     */
132    private MimeMessage buildTestMessage(String to, String cc, String bcc, String replyTo,
133            String sender, String subject, String content) throws MessagingException {
134        MimeMessage message = new MimeMessage();
135
136        if (to != null) {
137            Address[] addresses = Address.parse(to);
138            message.setRecipients(RecipientType.TO, addresses);
139        }
140        if (cc != null) {
141            Address[] addresses = Address.parse(cc);
142            message.setRecipients(RecipientType.CC, addresses);
143        }
144        if (bcc != null) {
145            Address[] addresses = Address.parse(bcc);
146            message.setRecipients(RecipientType.BCC, addresses);
147        }
148        if (replyTo != null) {
149            Address[] addresses = Address.parse(replyTo);
150            message.setReplyTo(addresses);
151        }
152        if (sender != null) {
153            Address[] addresses = Address.parse(sender);
154            message.setFrom(addresses[0]);
155        }
156        if (subject != null) {
157            message.setSubject(subject);
158        }
159        if (content != null) {
160            TextBody body = new TextBody(content);
161            message.setBody(body);
162        }
163
164        message.setUid(UID);
165        message.setSentDate(new Date());
166        message.setInternalDate(new Date());
167        message.setMessageId(MESSAGE_ID);
168        return message;
169    }
170
171    /**
172     * Basic test of body parts conversion from Store message to Provider message.
173     * This tests that a null body part simply results in null text, and does not crash
174     * or return "null".
175     *
176     * TODO very incomplete, there are many permutations to be explored
177     */
178    public void testUpdateBodyFieldsNullText() throws MessagingException {
179        EmailContent.Body localBody = new EmailContent.Body();
180        EmailContent.Message localMessage = new EmailContent.Message();
181        ArrayList<Part> viewables = new ArrayList<Part>();
182        Part emptyTextPart = new MimeBodyPart(null, "text/plain");
183        viewables.add(emptyTextPart);
184
185        // a "null" body part of type text/plain should result in a null mTextContent
186        final BodyFieldData data =
187                ConversionUtilities.parseBodyFields(viewables);
188        assertNull(data.textContent);
189    }
190
191    /**
192     * Sunny day test of adding attachments from an IMAP/POP message.
193     */
194    public void testAddAttachments() throws MessagingException, IOException {
195        // Prepare a local message to add the attachments to
196        final long accountId = 1;
197        final long mailboxId = 1;
198
199        // test 1: legacy message using content-type:name style for name
200        final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
201                "local-message", accountId, mailboxId, false, true, mProviderContext);
202        final Message legacyMessage = prepareLegacyMessageWithAttachments(2, false);
203        convertAndCheckcheckAddedAttachments(localMessage, legacyMessage);
204
205        // test 2: legacy message using content-disposition:filename style for name
206        final EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage(
207                "local-message", accountId, mailboxId, false, true, mProviderContext);
208        final Message legacyMessage2 = prepareLegacyMessageWithAttachments(2, true);
209        convertAndCheckcheckAddedAttachments(localMessage2, legacyMessage2);
210    }
211
212    /**
213     * Helper for testAddAttachments
214     */
215    private void convertAndCheckcheckAddedAttachments(final EmailContent.Message localMessage,
216            final Message legacyMessage) throws MessagingException, IOException {
217        // Now, convert from legacy to provider and see what happens
218        ArrayList<Part> viewables = new ArrayList<Part>();
219        ArrayList<Part> attachments = new ArrayList<Part>();
220        MimeUtility.collectParts(legacyMessage, viewables, attachments);
221        LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
222
223        // Read back all attachments for message and check field values
224        Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
225        Cursor c = mProviderContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION,
226                null, null, null);
227        try {
228            assertEquals(2, c.getCount());
229            while (c.moveToNext()) {
230                Attachment attachment = Attachment.getContent(c, Attachment.class);
231                if ("100".equals(attachment.mLocation)) {
232                    checkAttachment("attachment1Part", attachments.get(0), attachment,
233                            localMessage.mAccountKey);
234                } else if ("101".equals(attachment.mLocation)) {
235                    checkAttachment("attachment2Part", attachments.get(1), attachment,
236                            localMessage.mAccountKey);
237                } else {
238                    fail("Unexpected attachment with location " + attachment.mLocation);
239                }
240            }
241        } finally {
242            c.close();
243        }
244    }
245
246    /**
247     * Test that only "attachment" or "inline" attachments are captured and added.
248     * @throws MessagingException
249     * @throws IOException
250     */
251    public void testAttachmentDispositions() throws MessagingException, IOException {
252        // Prepare a local message to add the attachments to
253        final long accountId = 1;
254        final long mailboxId = 1;
255
256        // Prepare the three attachments we want to test
257        BodyPart[] sourceAttachments = new BodyPart[3];
258        BodyPart attachmentPart;
259
260        // 1. Standard attachment
261        attachmentPart = MessageTestUtils.bodyPart("image/jpg", null);
262        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg");
263        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
264        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
265                "attachment;\n filename=\"file-1\";\n size=100");
266        attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "100");
267        sourceAttachments[0] = attachmentPart;
268
269        // 2. Inline attachment
270        attachmentPart = MessageTestUtils.bodyPart("image/gif", null);
271        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/gif");
272        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
273        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
274                "inline;\n filename=\"file-2\";\n size=200");
275        attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "101");
276        sourceAttachments[1] = attachmentPart;
277
278        // 3. Neither (use VCALENDAR)
279        attachmentPart = MessageTestUtils.bodyPart("text/calendar", null);
280        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
281                "text/calendar; charset=UTF-8; method=REQUEST");
282        attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "7bit");
283        attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "102");
284        sourceAttachments[2] = attachmentPart;
285
286        // Prepare local message (destination) and legacy message w/attachments (source)
287        final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
288                "local-message", accountId, mailboxId, false, true, mProviderContext);
289        final Message legacyMessage = prepareLegacyMessageWithAttachments(sourceAttachments);
290        convertAndCheckcheckAddedAttachments(localMessage, legacyMessage);
291
292        // Run the conversion and check for the converted attachments - this test asserts
293        // that there are two attachments numbered 100 & 101 (so will fail if it finds 102)
294        convertAndCheckcheckAddedAttachments(localMessage, legacyMessage);
295    }
296
297    /**
298     * Test that attachments aren't re-added in the DB.  This supports the "partial download"
299     * nature of POP messages.
300     */
301    public void testAddDuplicateAttachments() throws MessagingException, IOException {
302        // Prepare a local message to add the attachments to
303        final long accountId = 1;
304        final long mailboxId = 1;
305        final EmailContent.Message localMessage = ProviderTestUtils.setupMessage(
306                "local-message", accountId, mailboxId, false, true, mProviderContext);
307
308        // Prepare a legacy message with attachments
309        Message legacyMessage = prepareLegacyMessageWithAttachments(2, false);
310
311        // Now, convert from legacy to provider and see what happens
312        ArrayList<Part> viewables = new ArrayList<Part>();
313        ArrayList<Part> attachments = new ArrayList<Part>();
314        MimeUtility.collectParts(legacyMessage, viewables, attachments);
315        LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
316
317        // Confirm two attachment objects created
318        Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId);
319        assertEquals(2, EmailContent.count(mProviderContext, uri, null, null));
320
321        // Now add the attachments again and confirm there are still only two
322        LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
323        assertEquals(2, EmailContent.count(mProviderContext, uri, null, null));
324
325        // Now add a 3rd & 4th attachment and make sure the total is 4, not 2 or 6
326        legacyMessage = prepareLegacyMessageWithAttachments(4, false);
327        viewables = new ArrayList<Part>();
328        attachments = new ArrayList<Part>();
329        MimeUtility.collectParts(legacyMessage, viewables, attachments);
330        LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments);
331        assertEquals(4, EmailContent.count(mProviderContext, uri, null, null));
332    }
333
334    /**
335     * Prepare a legacy message with 1+ attachments
336     * @param numAttachments how many attachments to add
337     * @param filenameInDisposition False: attachment names are sent as content-type:name.  True:
338     *          attachment names are sent as content-disposition:filename.
339     */
340    private Message prepareLegacyMessageWithAttachments(int numAttachments,
341            boolean filenameInDisposition) throws MessagingException {
342        BodyPart[] attachmentParts = new BodyPart[numAttachments];
343        for (int i = 0; i < numAttachments; ++i) {
344            // construct parameter parts for content-type:name or content-disposition:filename.
345            String name = "";
346            String filename = "";
347            String quotedName = "\"test-attachment-" + i + "\"";
348            if (filenameInDisposition) {
349                filename = ";\n filename=" + quotedName;
350            } else {
351                name = ";\n name=" + quotedName;
352            }
353
354            // generate an attachment that came from a server
355            BodyPart attachmentPart = MessageTestUtils.bodyPart("image/jpg", null);
356
357            // name=attachmentN size=N00 location=10N
358            attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "image/jpg" + name);
359            attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
360            attachmentPart.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION,
361                    "attachment" + filename +  ";\n size=" + (i+1) + "00");
362            attachmentPart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "10" + i);
363
364            attachmentParts[i] = attachmentPart;
365        }
366
367        return prepareLegacyMessageWithAttachments(attachmentParts);
368    }
369
370    /**
371     * Prepare a legacy message with 1+ attachments
372     * @param attachments array containing one or more attachments
373     */
374    private Message prepareLegacyMessageWithAttachments(BodyPart[] attachments)
375            throws MessagingException {
376        // Build the multipart that holds the attachments
377        MultipartBuilder mpBuilder = new MultipartBuilder("multipart/mixed");
378        for (int i = 0; i < attachments.length; ++i) {
379            mpBuilder.addBodyPart(attachments[i]);
380        }
381
382        // Now build a message with them
383        final Message legacyMessage = new MessageBuilder()
384            .setBody(new MultipartBuilder("multipart/mixed")
385                     .addBodyPart(MessageTestUtils.bodyPart("text/html", null))
386                     .addBodyPart(mpBuilder.buildBodyPart())
387                     .build())
388                .build();
389
390        return legacyMessage;
391    }
392
393    /**
394     * Test the stringInequal helper
395     */
396    public void testStringInequal() {
397        // Pairs that are "equal"
398        assertFalse(LegacyConversions.stringNotEqual(null, null));
399        assertFalse(LegacyConversions.stringNotEqual(null, ""));
400        assertFalse(LegacyConversions.stringNotEqual("", null));
401        assertFalse(LegacyConversions.stringNotEqual("", ""));
402        assertFalse(LegacyConversions.stringNotEqual("string-equal", "string-equal"));
403        // Pairs that are "inequal"
404        assertTrue(LegacyConversions.stringNotEqual(null, "string-inequal"));
405        assertTrue(LegacyConversions.stringNotEqual("", "string-inequal"));
406        assertTrue(LegacyConversions.stringNotEqual("string-inequal", null));
407        assertTrue(LegacyConversions.stringNotEqual("string-inequal", ""));
408        assertTrue(LegacyConversions.stringNotEqual("string-inequal-a", "string-inequal-b"));
409    }
410
411    /**
412     * Compare attachment that was converted from Part (expected) to Provider Attachment (actual)
413     *
414     * TODO content URI should only be set if we also saved a file
415     * TODO other data encodings
416     */
417    private void checkAttachment(String tag, Part expected, EmailContent.Attachment actual,
418            long accountKey) throws MessagingException {
419        String contentType = MimeUtility.unfoldAndDecode(expected.getContentType());
420        String contentTypeName = MimeUtility.getHeaderParameter(contentType, "name");
421        assertEquals(tag, expected.getMimeType(), actual.mMimeType);
422        String disposition = expected.getDisposition();
423        String sizeString = MimeUtility.getHeaderParameter(disposition, "size");
424        String dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename");
425        long expectedSize = (sizeString != null) ? Long.parseLong(sizeString) : 0;
426        assertEquals(tag, expectedSize, actual.mSize);
427        assertEquals(tag, expected.getContentId(), actual.mContentId);
428
429        // filename is either content-type:name or content-disposition:filename
430        String expectedName = (contentTypeName != null) ? contentTypeName : dispositionFilename;
431        assertEquals(tag, expectedName, actual.mFileName);
432
433        // content URI should be null
434        assertNull(tag, actual.mContentUri);
435
436        assertTrue(tag, 0 != actual.mMessageKey);
437
438        // location is either both null or both matching
439        String expectedPartId = null;
440        String[] storeData = expected.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
441        if (storeData != null && storeData.length > 0) {
442            expectedPartId = storeData[0];
443        }
444        assertEquals(tag, expectedPartId, actual.mLocation);
445        assertEquals(tag, "B", actual.mEncoding);
446        assertEquals(tag, accountKey, actual.mAccountKey);
447    }
448
449    /**
450     * TODO: Sunny day test of adding attachments from a POP message.
451     */
452
453    /**
454     * Sunny day tests of converting an original message to a legacy message
455     */
456    public void testMakeLegacyMessage() throws MessagingException {
457        // Set up and store a message in the provider
458        long account1Id = 1;
459        long mailbox1Id = 1;
460
461        // Test message 1: No body
462        EmailContent.Message localMessage1 = ProviderTestUtils.setupMessage("make-legacy",
463                account1Id, mailbox1Id, false, true, mProviderContext);
464        Message getMessage1 = LegacyConversions.makeMessage(mProviderContext, localMessage1);
465        checkLegacyMessage("no body", localMessage1, getMessage1);
466
467        // Test message 2: Simple body
468        EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage("make-legacy",
469                account1Id, mailbox1Id, true, false, mProviderContext);
470        localMessage2.mTextReply = null;
471        localMessage2.mHtmlReply = null;
472        localMessage2.mIntroText = null;
473        localMessage2.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
474        localMessage2.save(mProviderContext);
475        Message getMessage2 = LegacyConversions.makeMessage(mProviderContext, localMessage2);
476        checkLegacyMessage("simple body", localMessage2, getMessage2);
477
478        // Test message 3: Body + replied-to text
479        EmailContent.Message localMessage3 = ProviderTestUtils.setupMessage("make-legacy",
480                account1Id, mailbox1Id, true, false, mProviderContext);
481        localMessage3.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
482        localMessage3.mFlags |= EmailContent.Message.FLAG_TYPE_REPLY;
483        localMessage3.save(mProviderContext);
484        Message getMessage3 = LegacyConversions.makeMessage(mProviderContext, localMessage3);
485        checkLegacyMessage("reply-to", localMessage3, getMessage3);
486
487        // Test message 4: Body + forwarded text
488        EmailContent.Message localMessage4 = ProviderTestUtils.setupMessage("make-legacy",
489                account1Id, mailbox1Id, true, false, mProviderContext);
490        localMessage4.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK;
491        localMessage4.mFlags |= EmailContent.Message.FLAG_TYPE_FORWARD;
492        localMessage4.save(mProviderContext);
493        Message getMessage4 = LegacyConversions.makeMessage(mProviderContext, localMessage4);
494        checkLegacyMessage("forwarding", localMessage4, getMessage4);
495    }
496
497    /**
498     * Check equality of a pair of converted messages
499     */
500    private void checkProviderMessage(String tag, Message expect, EmailContent.Message actual)
501            throws MessagingException {
502        assertEquals(tag, expect.getUid(), actual.mServerId);
503        assertEquals(tag, expect.getSubject(), actual.mSubject);
504        assertEquals(tag, Address.pack(expect.getFrom()), actual.mFrom);
505        assertEquals(tag, expect.getSentDate().getTime(), actual.mTimeStamp);
506        assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.TO)), actual.mTo);
507        assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.CC)), actual.mCc);
508        assertEquals(tag, ((MimeMessage)expect).getMessageId(), actual.mMessageId);
509        assertEquals(tag, expect.isSet(Flag.SEEN), actual.mFlagRead);
510        assertEquals(tag, expect.isSet(Flag.FLAGGED), actual.mFlagFavorite);
511    }
512
513    /**
514     * Check equality of a pair of converted messages
515     */
516    private void checkLegacyMessage(String tag, EmailContent.Message expect, Message actual)
517            throws MessagingException {
518        assertEquals(tag, expect.mServerId, actual.getUid());
519        assertEquals(tag, expect.mServerTimeStamp, actual.getInternalDate().getTime());
520        assertEquals(tag, expect.mSubject, actual.getSubject());
521        assertEquals(tag, expect.mFrom, Address.pack(actual.getFrom()));
522        assertEquals(tag, expect.mTimeStamp, actual.getSentDate().getTime());
523        assertEquals(tag, expect.mTo, Address.pack(actual.getRecipients(RecipientType.TO)));
524        assertEquals(tag, expect.mCc, Address.pack(actual.getRecipients(RecipientType.CC)));
525        assertEquals(tag, expect.mBcc, Address.pack(actual.getRecipients(RecipientType.BCC)));
526        assertEquals(tag, expect.mReplyTo, Address.pack(actual.getReplyTo()));
527        assertEquals(tag, expect.mMessageId, ((MimeMessage)actual).getMessageId());
528        // check flags
529        assertEquals(tag, expect.mFlagRead, actual.isSet(Flag.SEEN));
530        assertEquals(tag, expect.mFlagFavorite, actual.isSet(Flag.FLAGGED));
531
532        // Check the body of the message
533        ArrayList<Part> viewables = new ArrayList<Part>();
534        ArrayList<Part> attachments = new ArrayList<Part>();
535        MimeUtility.collectParts(actual, viewables, attachments);
536        String get1Text = null;
537        String get1Html = null;
538        String get1TextReply = null;
539        String get1HtmlReply = null;
540        String get1TextIntro = null;
541        for (Part viewable : viewables) {
542            String text = MimeUtility.getTextFromPart(viewable);
543            boolean isHtml = viewable.getMimeType().equalsIgnoreCase("text/html");
544            String[] headers = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART);
545            if (headers != null) {
546                String header = headers[0];
547                boolean isReply = LegacyConversions.BODY_QUOTED_PART_REPLY.equalsIgnoreCase(header);
548                boolean isFwd = LegacyConversions.BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(header);
549                boolean isIntro = LegacyConversions.BODY_QUOTED_PART_INTRO.equalsIgnoreCase(header);
550                if (isReply || isFwd) {
551                    if (isHtml) {
552                        get1HtmlReply = text;
553                    } else {
554                        get1TextReply = text;
555                    }
556                } else if (isIntro) {
557                    get1TextIntro = text;
558                }
559                // Check flags
560                int replyTypeFlags = expect.mFlags & EmailContent.Message.FLAG_TYPE_MASK;
561                if (isReply) {
562                    assertEquals(tag, EmailContent.Message.FLAG_TYPE_REPLY, replyTypeFlags);
563                }
564                if (isFwd) {
565                    assertEquals(tag, EmailContent.Message.FLAG_TYPE_FORWARD, replyTypeFlags);
566                }
567            } else {
568                if (isHtml) {
569                    get1Html = text;
570                } else {
571                    get1Text = text;
572                }
573            }
574        }
575        assertEquals(tag, expect.mText, get1Text);
576        assertEquals(tag, expect.mHtml, get1Html);
577        assertEquals(tag, expect.mTextReply, get1TextReply);
578        assertEquals(tag, expect.mHtmlReply, get1HtmlReply);
579        assertEquals(tag, expect.mIntroText, get1TextIntro);
580
581        // TODO Check the attachments
582
583//      cv.put("attachment_count", attachments.size());
584    }
585}
586