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