LegacyConversionsTests.java revision b6756688b1bf07c50b999c9d30dd6cb224d3812b
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.mail.Address; 20import com.android.email.mail.BodyPart; 21import com.android.email.mail.Flag; 22import com.android.email.mail.Message; 23import com.android.email.mail.MessageTestUtils; 24import com.android.email.mail.MessagingException; 25import com.android.email.mail.Part; 26import com.android.email.mail.Message.RecipientType; 27import com.android.email.mail.MessageTestUtils.MessageBuilder; 28import com.android.email.mail.MessageTestUtils.MultipartBuilder; 29import com.android.email.mail.internet.MimeBodyPart; 30import com.android.email.mail.internet.MimeHeader; 31import com.android.email.mail.internet.MimeMessage; 32import com.android.email.mail.internet.MimeUtility; 33import com.android.email.mail.internet.TextBody; 34import com.android.email.provider.EmailContent; 35import com.android.email.provider.EmailProvider; 36import com.android.email.provider.ProviderTestUtils; 37import com.android.email.provider.EmailContent.Attachment; 38 39import android.content.ContentUris; 40import android.content.Context; 41import android.database.Cursor; 42import android.net.Uri; 43import android.test.ProviderTestCase2; 44 45import java.io.IOException; 46import java.util.ArrayList; 47import java.util.Date; 48 49/** 50 * Tests of the Legacy Conversions code (used by MessagingController). 51 * 52 * NOTE: It would probably make sense to rewrite this using a MockProvider, instead of the 53 * ProviderTestCase (which is a real provider running on a temp database). This would be more of 54 * a true "unit test". 55 * 56 * You can run this entire test case with: 57 * runtest -c com.android.email.LegacyConversionsTests email 58 */ 59public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> { 60 61 private static final String UID = "UID.12345678"; 62 private static final String SENDER = "sender@android.com"; 63 private static final String RECIPIENT_TO = "recipient-to@android.com"; 64 private static final String RECIPIENT_CC = "recipient-cc@android.com"; 65 private static final String RECIPIENT_BCC = "recipient-bcc@android.com"; 66 private static final String REPLY_TO = "reply-to@android.com"; 67 private static final String SUBJECT = "This is the subject"; 68 private static final String BODY = "This is the body. This is also the body."; 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 76 public LegacyConversionsTests() { 77 super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY); 78 } 79 80 @Override 81 public void setUp() throws Exception { 82 super.setUp(); 83 mProviderContext = getMockContext(); 84 mContext = getContext(); 85 } 86 87 @Override 88 public void tearDown() throws Exception { 89 super.tearDown(); 90 } 91 92 /** 93 * TODO: basic Legacy -> Provider Message conversions 94 * TODO: basic Legacy -> Provider Body conversions 95 * TODO: rainy day tests of all kinds 96 */ 97 98 /** 99 * Test basic conversion from Store message to Provider message 100 * 101 * TODO: Not a complete test of all fields, and some fields need special tests (e.g. flags) 102 * TODO: There are many special cases in the tested function, that need to be 103 * tested here as well. 104 */ 105 public void testUpdateMessageFields() throws MessagingException { 106 MimeMessage message = buildTestMessage(RECIPIENT_TO, RECIPIENT_CC, RECIPIENT_BCC, 107 REPLY_TO, SENDER, SUBJECT, null); 108 EmailContent.Message localMessage = new EmailContent.Message(); 109 110 boolean result = LegacyConversions.updateMessageFields(localMessage, message, 1, 1); 111 assertTrue(result); 112 checkProviderMessage("testUpdateMessageFields", message, localMessage); 113 } 114 115 /** 116 * Test basic conversion from Store message to Provider message, when the provider message 117 * does not have a proper message-id. 118 */ 119 public void testUpdateMessageFieldsNoMessageId() throws MessagingException { 120 MimeMessage message = buildTestMessage(RECIPIENT_TO, RECIPIENT_CC, RECIPIENT_BCC, 121 REPLY_TO, SENDER, SUBJECT, null); 122 EmailContent.Message localMessage = new EmailContent.Message(); 123 124 // If the source message-id is null, the target should be left as-is 125 localMessage.mMessageId = MESSAGE_ID_2; 126 message.removeHeader("Message-ID"); 127 128 boolean result = LegacyConversions.updateMessageFields(localMessage, message, 1, 1); 129 assertTrue(result); 130 assertEquals(MESSAGE_ID_2, localMessage.mMessageId); 131 } 132 133 /** 134 * Build a lightweight Store message with simple field population 135 */ 136 private MimeMessage buildTestMessage(String to, String cc, String bcc, String replyTo, 137 String sender, String subject, String content) throws MessagingException { 138 MimeMessage message = new MimeMessage(); 139 140 if (to != null) { 141 Address[] addresses = Address.parse(to); 142 message.setRecipients(RecipientType.TO, addresses); 143 } 144 if (cc != null) { 145 Address[] addresses = Address.parse(cc); 146 message.setRecipients(RecipientType.CC, addresses); 147 } 148 if (bcc != null) { 149 Address[] addresses = Address.parse(bcc); 150 message.setRecipients(RecipientType.BCC, addresses); 151 } 152 if (replyTo != null) { 153 Address[] addresses = Address.parse(replyTo); 154 message.setReplyTo(addresses); 155 } 156 if (sender != null) { 157 Address[] addresses = Address.parse(sender); 158 message.setFrom(Address.parse(sender)[0]); 159 } 160 if (subject != null) { 161 message.setSubject(subject); 162 } 163 if (content != null) { 164 TextBody body = new TextBody(content); 165 message.setBody(body); 166 } 167 168 message.setUid(UID); 169 message.setSentDate(new Date()); 170 message.setInternalDate(new Date()); 171 message.setMessageId(MESSAGE_ID); 172 return message; 173 } 174 175 /** 176 * Basic test of body parts conversion from Store message to Provider message. 177 * This tests that a null body part simply results in null text, and does not crash 178 * or return "null". 179 * 180 * TODO very incomplete, there are many permutations to be explored 181 */ 182 public void testUpdateBodyFieldsNullText() throws MessagingException { 183 EmailContent.Body localBody = new EmailContent.Body(); 184 EmailContent.Message localMessage = new EmailContent.Message(); 185 ArrayList<Part> viewables = new ArrayList<Part>(); 186 Part emptyTextPart = new MimeBodyPart(null, "text/plain"); 187 viewables.add(emptyTextPart); 188 189 // a "null" body part of type text/plain should result in a null mTextContent 190 boolean result = LegacyConversions.updateBodyFields(localBody, localMessage, viewables); 191 assertTrue(result); 192 assertNull(localBody.mTextContent); 193 } 194 195 /** 196 * Sunny day test of adding attachments from an IMAP message. 197 */ 198 public void testAddAttachments() throws MessagingException, IOException { 199 // Prepare a local message to add the attachments to 200 final long accountId = 1; 201 final long mailboxId = 1; 202 final EmailContent.Message localMessage = ProviderTestUtils.setupMessage( 203 "local-message", accountId, mailboxId, false, true, mProviderContext); 204 205 // Prepare a legacy message with attachments 206 Part attachment1Part = MessageTestUtils.bodyPart("image/gif", null); 207 attachment1Part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, 208 "image/gif;\n name=\"attachment1\""); 209 attachment1Part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); 210 attachment1Part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, 211 "attachment;\n filename=\"attachment1\";\n size=100"); 212 attachment1Part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "101"); 213 214 Part attachment2Part = MessageTestUtils.bodyPart("image/jpg", null); 215 attachment2Part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, 216 "image/jpg;\n name=\"attachment2\""); 217 attachment2Part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); 218 attachment2Part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, 219 "attachment;\n filename=\"attachment2\";\n size=200"); 220 attachment2Part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "102"); 221 222 final Message legacyMessage = new MessageBuilder() 223 .setBody(new MultipartBuilder("multipart/mixed") 224 .addBodyPart(MessageTestUtils.bodyPart("text/html", null)) 225 .addBodyPart(new MultipartBuilder("multipart/mixed") 226 .addBodyPart((BodyPart)attachment1Part) 227 .addBodyPart((BodyPart)attachment2Part) 228 .buildBodyPart()) 229 .build()) 230 .build(); 231 232 // Now, convert from legacy to provider and see what happens 233 ArrayList<Part> viewables = new ArrayList<Part>(); 234 ArrayList<Part> attachments = new ArrayList<Part>(); 235 MimeUtility.collectParts(legacyMessage, viewables, attachments); 236 LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments); 237 238 // Read back all attachments for message and check field values 239 Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId); 240 Cursor c = mProviderContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION, 241 null, null, null); 242 try { 243 assertEquals(2, c.getCount()); 244 while (c.moveToNext()) { 245 Attachment attachment = Attachment.getContent(c, Attachment.class); 246 if ("101".equals(attachment.mLocation)) { 247 checkAttachment("attachment1Part", attachment1Part, attachment); 248 } else if ("102".equals(attachment.mLocation)) { 249 checkAttachment("attachment2Part", attachment2Part, attachment); 250 } else { 251 fail("Unexpected attachment with location " + attachment.mLocation); 252 } 253 } 254 } finally { 255 c.close(); 256 } 257 } 258 259 /** 260 * Compare attachment that was converted from Part (expected) to Provider Attachment (actual) 261 * 262 * TODO content URI should only be set if we also saved a file 263 * TODO other data encodings 264 */ 265 private void checkAttachment(String tag, Part expected, EmailContent.Attachment actual) 266 throws MessagingException { 267 String contentType = MimeUtility.unfoldAndDecode(expected.getContentType()); 268 String expectedName = MimeUtility.getHeaderParameter(contentType, "name"); 269 assertEquals(tag, expectedName, actual.mFileName); 270 assertEquals(tag, expected.getMimeType(), actual.mMimeType); 271 String disposition = expected.getDisposition(); 272 String sizeString = MimeUtility.getHeaderParameter(disposition, "size"); 273 long expectedSize = Long.parseLong(sizeString); 274 assertEquals(tag, expectedSize, actual.mSize); 275 assertEquals(tag, expected.getContentId(), actual.mContentId); 276 assertNull(tag, actual.mContentUri); 277 assertTrue(tag, 0 != actual.mMessageKey); 278 String expectedPartId = 279 expected.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA)[0]; 280 assertEquals(tag, expectedPartId, actual.mLocation); 281 assertEquals(tag, "B", actual.mEncoding); 282 } 283 284 /** 285 * TODO: Sunny day test of adding attachments from a POP message. 286 */ 287 288 /** 289 * Sunny day tests of converting an original message to a legacy message 290 */ 291 public void testMakeLegacyMessage() throws MessagingException { 292 // Set up and store a message in the provider 293 long account1Id = 1; 294 long mailbox1Id = 1; 295 296 // Test message 1: No body 297 EmailContent.Message localMessage1 = ProviderTestUtils.setupMessage("make-legacy", 298 account1Id, mailbox1Id, false, true, mProviderContext); 299 Message getMessage1 = LegacyConversions.makeMessage(mProviderContext, localMessage1); 300 checkLegacyMessage("no body", localMessage1, getMessage1); 301 302 // Test message 2: Simple body 303 EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage("make-legacy", 304 account1Id, mailbox1Id, true, false, mProviderContext); 305 localMessage2.mTextReply = null; 306 localMessage2.mHtmlReply = null; 307 localMessage2.mIntroText = null; 308 localMessage2.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK; 309 localMessage2.save(mProviderContext); 310 Message getMessage2 = LegacyConversions.makeMessage(mProviderContext, localMessage2); 311 checkLegacyMessage("simple body", localMessage2, getMessage2); 312 313 // Test message 3: Body + replied-to text 314 EmailContent.Message localMessage3 = ProviderTestUtils.setupMessage("make-legacy", 315 account1Id, mailbox1Id, true, false, mProviderContext); 316 localMessage3.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK; 317 localMessage3.mFlags |= EmailContent.Message.FLAG_TYPE_REPLY; 318 localMessage3.save(mProviderContext); 319 Message getMessage3 = LegacyConversions.makeMessage(mProviderContext, localMessage3); 320 checkLegacyMessage("reply-to", localMessage3, getMessage3); 321 322 // Test message 4: Body + forwarded text 323 EmailContent.Message localMessage4 = ProviderTestUtils.setupMessage("make-legacy", 324 account1Id, mailbox1Id, true, false, mProviderContext); 325 localMessage4.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK; 326 localMessage4.mFlags |= EmailContent.Message.FLAG_TYPE_FORWARD; 327 localMessage4.save(mProviderContext); 328 Message getMessage4 = LegacyConversions.makeMessage(mProviderContext, localMessage4); 329 checkLegacyMessage("forwarding", localMessage4, getMessage4); 330 } 331 332 /** 333 * Check equality of a pair of converted messages 334 */ 335 private void checkProviderMessage(String tag, Message expect, EmailContent.Message actual) 336 throws MessagingException { 337 assertEquals(tag, expect.getUid(), actual.mServerId); 338 assertEquals(tag, expect.getSubject(), actual.mSubject); 339 assertEquals(tag, Address.pack(expect.getFrom()), actual.mFrom); 340 assertEquals(tag, expect.getSentDate().getTime(), actual.mTimeStamp); 341 assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.TO)), actual.mTo); 342 assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.CC)), actual.mCc); 343 assertEquals(tag, ((MimeMessage)expect).getMessageId(), actual.mMessageId); 344 assertEquals(tag, expect.isSet(Flag.SEEN), actual.mFlagRead); 345 assertEquals(tag, expect.isSet(Flag.FLAGGED), actual.mFlagFavorite); 346 } 347 348 /** 349 * Check equality of a pair of converted messages 350 */ 351 private void checkLegacyMessage(String tag, EmailContent.Message expect, Message actual) 352 throws MessagingException { 353 assertEquals(tag, expect.mServerId, actual.getUid()); 354 assertEquals(tag, expect.mServerTimeStamp, actual.getInternalDate().getTime()); 355 assertEquals(tag, expect.mSubject, actual.getSubject()); 356 assertEquals(tag, expect.mFrom, Address.pack(actual.getFrom())); 357 assertEquals(tag, expect.mTimeStamp, actual.getSentDate().getTime()); 358 assertEquals(tag, expect.mTo, Address.pack(actual.getRecipients(RecipientType.TO))); 359 assertEquals(tag, expect.mCc, Address.pack(actual.getRecipients(RecipientType.CC))); 360 assertEquals(tag, expect.mBcc, Address.pack(actual.getRecipients(RecipientType.BCC))); 361 assertEquals(tag, expect.mReplyTo, Address.pack(actual.getReplyTo())); 362 assertEquals(tag, expect.mMessageId, ((MimeMessage)actual).getMessageId()); 363 // check flags 364 assertEquals(tag, expect.mFlagRead, actual.isSet(Flag.SEEN)); 365 assertEquals(tag, expect.mFlagFavorite, actual.isSet(Flag.FLAGGED)); 366 367 // Check the body of the message 368 ArrayList<Part> viewables = new ArrayList<Part>(); 369 ArrayList<Part> attachments = new ArrayList<Part>(); 370 MimeUtility.collectParts(actual, viewables, attachments); 371 String get1Text = null; 372 String get1Html = null; 373 String get1TextReply = null; 374 String get1HtmlReply = null; 375 String get1TextIntro = null; 376 for (Part viewable : viewables) { 377 String text = MimeUtility.getTextFromPart(viewable); 378 boolean isHtml = viewable.getMimeType().equalsIgnoreCase("text/html"); 379 String[] headers = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART); 380 if (headers != null) { 381 String header = headers[0]; 382 boolean isReply = LegacyConversions.BODY_QUOTED_PART_REPLY.equalsIgnoreCase(header); 383 boolean isFwd = LegacyConversions.BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(header); 384 boolean isIntro = LegacyConversions.BODY_QUOTED_PART_INTRO.equalsIgnoreCase(header); 385 if (isReply || isFwd) { 386 if (isHtml) { 387 get1HtmlReply = text; 388 } else { 389 get1TextReply = text; 390 } 391 } else if (isIntro) { 392 get1TextIntro = text; 393 } 394 // Check flags 395 int replyTypeFlags = expect.mFlags & EmailContent.Message.FLAG_TYPE_MASK; 396 if (isReply) { 397 assertEquals(tag, EmailContent.Message.FLAG_TYPE_REPLY, replyTypeFlags); 398 } 399 if (isFwd) { 400 assertEquals(tag, EmailContent.Message.FLAG_TYPE_FORWARD, replyTypeFlags); 401 } 402 } else { 403 if (isHtml) { 404 get1Html = text; 405 } else { 406 get1Text = text; 407 } 408 } 409 } 410 assertEquals(tag, expect.mText, get1Text); 411 assertEquals(tag, expect.mHtml, get1Html); 412 assertEquals(tag, expect.mTextReply, get1TextReply); 413 assertEquals(tag, expect.mHtmlReply, get1HtmlReply); 414 assertEquals(tag, expect.mIntroText, get1TextIntro); 415 416 // TODO Check the attachments 417 418// cv.put("attachment_count", attachments.size()); 419 } 420} 421