LegacyConversionsTests.java revision eb7752bf695b2a93854e0bb89ddbbc2236bb9aea
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.MimeHeader; 30import com.android.email.mail.internet.MimeMessage; 31import com.android.email.mail.internet.MimeUtility; 32import com.android.email.mail.internet.TextBody; 33import com.android.email.provider.EmailContent; 34import com.android.email.provider.EmailProvider; 35import com.android.email.provider.ProviderTestUtils; 36import com.android.email.provider.EmailContent.Attachment; 37 38import android.content.ContentUris; 39import android.content.Context; 40import android.database.Cursor; 41import android.net.Uri; 42import android.test.ProviderTestCase2; 43 44import java.io.IOException; 45import java.util.ArrayList; 46import java.util.Date; 47 48/** 49 * Tests of the Legacy Conversions code (used by MessagingController). 50 * 51 * NOTE: It would probably make sense to rewrite this using a MockProvider, instead of the 52 * ProviderTestCase (which is a real provider running on a temp database). This would be more of 53 * a true "unit test". 54 * 55 * You can run this entire test case with: 56 * runtest -c com.android.email.LegacyConversionsTests email 57 */ 58public class LegacyConversionsTests extends ProviderTestCase2<EmailProvider> { 59 60 private static final String UID = "UID.12345678"; 61 private static final String SENDER = "sender@android.com"; 62 private static final String RECIPIENT_TO = "recipient-to@android.com"; 63 private static final String RECIPIENT_CC = "recipient-cc@android.com"; 64 private static final String RECIPIENT_BCC = "recipient-bcc@android.com"; 65 private static final String REPLY_TO = "reply-to@android.com"; 66 private static final String SUBJECT = "This is the subject"; 67 private static final String BODY = "This is the body. This is also the body."; 68 private static final String MESSAGE_ID = "Test-Message-ID"; 69 private static final String MESSAGE_ID_2 = "Test-Message-ID-Second"; 70 71 EmailProvider mProvider; 72 Context mProviderContext; 73 Context mContext; 74 75 public LegacyConversionsTests() { 76 super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY); 77 } 78 79 @Override 80 public void setUp() throws Exception { 81 super.setUp(); 82 mProviderContext = getMockContext(); 83 mContext = getContext(); 84 } 85 86 @Override 87 public void tearDown() throws Exception { 88 super.tearDown(); 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(Address.parse(sender)[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 * Sunny day test of adding attachments from an IMAP message. 176 */ 177 public void testAddAttachments() throws MessagingException, IOException { 178 // Prepare a local message to add the attachments to 179 final long accountId = 1; 180 final long mailboxId = 1; 181 final EmailContent.Message localMessage = ProviderTestUtils.setupMessage( 182 "local-message", accountId, mailboxId, false, true, mProviderContext); 183 184 // Prepare a legacy message with attachments 185 Part attachment1Part = MessageTestUtils.bodyPart("image/gif", null); 186 attachment1Part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, 187 "image/gif;\n name=\"attachment1\""); 188 attachment1Part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); 189 attachment1Part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, 190 "attachment;\n filename=\"attachment1\";\n size=100"); 191 attachment1Part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "101"); 192 193 Part attachment2Part = MessageTestUtils.bodyPart("image/jpg", null); 194 attachment2Part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, 195 "image/jpg;\n name=\"attachment2\""); 196 attachment2Part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); 197 attachment2Part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, 198 "attachment;\n filename=\"attachment2\";\n size=200"); 199 attachment2Part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, "102"); 200 201 final Message legacyMessage = new MessageBuilder() 202 .setBody(new MultipartBuilder("multipart/mixed") 203 .addBodyPart(MessageTestUtils.bodyPart("text/html", null)) 204 .addBodyPart(new MultipartBuilder("multipart/mixed") 205 .addBodyPart((BodyPart)attachment1Part) 206 .addBodyPart((BodyPart)attachment2Part) 207 .buildBodyPart()) 208 .build()) 209 .build(); 210 211 // Now, convert from legacy to provider and see what happens 212 ArrayList<Part> viewables = new ArrayList<Part>(); 213 ArrayList<Part> attachments = new ArrayList<Part>(); 214 MimeUtility.collectParts(legacyMessage, viewables, attachments); 215 LegacyConversions.updateAttachments(mProviderContext, localMessage, attachments); 216 217 // Read back all attachments for message and check field values 218 Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, localMessage.mId); 219 Cursor c = mProviderContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION, 220 null, null, null); 221 try { 222 assertEquals(2, c.getCount()); 223 while (c.moveToNext()) { 224 Attachment attachment = Attachment.getContent(c, Attachment.class); 225 if ("101".equals(attachment.mLocation)) { 226 checkAttachment("attachment1Part", attachment1Part, attachment); 227 } else if ("102".equals(attachment.mLocation)) { 228 checkAttachment("attachment2Part", attachment2Part, attachment); 229 } else { 230 fail("Unexpected attachment with location " + attachment.mLocation); 231 } 232 } 233 } finally { 234 c.close(); 235 } 236 } 237 238 /** 239 * Compare attachment that was converted from Part (expected) to Provider Attachment (actual) 240 * 241 * TODO content URI should only be set if we also saved a file 242 * TODO other data encodings 243 */ 244 private void checkAttachment(String tag, Part expected, EmailContent.Attachment actual) 245 throws MessagingException { 246 String contentType = MimeUtility.unfoldAndDecode(expected.getContentType()); 247 String expectedName = MimeUtility.getHeaderParameter(contentType, "name"); 248 assertEquals(tag, expectedName, actual.mFileName); 249 assertEquals(tag, expected.getMimeType(), actual.mMimeType); 250 String disposition = expected.getDisposition(); 251 String sizeString = MimeUtility.getHeaderParameter(disposition, "size"); 252 long expectedSize = Long.parseLong(sizeString); 253 assertEquals(tag, expectedSize, actual.mSize); 254 assertEquals(tag, expected.getContentId(), actual.mContentId); 255 assertNull(tag, actual.mContentUri); 256 assertTrue(tag, 0 != actual.mMessageKey); 257 String expectedPartId = 258 expected.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA)[0]; 259 assertEquals(tag, expectedPartId, actual.mLocation); 260 assertEquals(tag, "B", actual.mEncoding); 261 } 262 263 /** 264 * TODO: Sunny day test of adding attachments from a POP message. 265 */ 266 267 /** 268 * Sunny day tests of converting an original message to a legacy message 269 */ 270 public void testMakeLegacyMessage() throws MessagingException { 271 // Set up and store a message in the provider 272 long account1Id = 1; 273 long mailbox1Id = 1; 274 275 // Test message 1: No body 276 EmailContent.Message localMessage1 = ProviderTestUtils.setupMessage("make-legacy", 277 account1Id, mailbox1Id, false, true, mProviderContext); 278 Message getMessage1 = LegacyConversions.makeMessage(mProviderContext, localMessage1); 279 checkLegacyMessage("no body", localMessage1, getMessage1); 280 281 // Test message 2: Simple body 282 EmailContent.Message localMessage2 = ProviderTestUtils.setupMessage("make-legacy", 283 account1Id, mailbox1Id, true, false, mProviderContext); 284 localMessage2.mTextReply = null; 285 localMessage2.mHtmlReply = null; 286 localMessage2.mIntroText = null; 287 localMessage2.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK; 288 localMessage2.save(mProviderContext); 289 Message getMessage2 = LegacyConversions.makeMessage(mProviderContext, localMessage2); 290 checkLegacyMessage("simple body", localMessage2, getMessage2); 291 292 // Test message 3: Body + replied-to text 293 EmailContent.Message localMessage3 = ProviderTestUtils.setupMessage("make-legacy", 294 account1Id, mailbox1Id, true, false, mProviderContext); 295 localMessage3.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK; 296 localMessage3.mFlags |= EmailContent.Message.FLAG_TYPE_REPLY; 297 localMessage3.save(mProviderContext); 298 Message getMessage3 = LegacyConversions.makeMessage(mProviderContext, localMessage3); 299 checkLegacyMessage("reply-to", localMessage3, getMessage3); 300 301 // Test message 4: Body + forwarded text 302 EmailContent.Message localMessage4 = ProviderTestUtils.setupMessage("make-legacy", 303 account1Id, mailbox1Id, true, false, mProviderContext); 304 localMessage4.mFlags &= ~EmailContent.Message.FLAG_TYPE_MASK; 305 localMessage4.mFlags |= EmailContent.Message.FLAG_TYPE_FORWARD; 306 localMessage4.save(mProviderContext); 307 Message getMessage4 = LegacyConversions.makeMessage(mProviderContext, localMessage4); 308 checkLegacyMessage("forwarding", localMessage4, getMessage4); 309 } 310 311 /** 312 * Check equality of a pair of converted messages 313 */ 314 private void checkProviderMessage(String tag, Message expect, EmailContent.Message actual) 315 throws MessagingException { 316 assertEquals(tag, expect.getUid(), actual.mServerId); 317 assertEquals(tag, expect.getSubject(), actual.mSubject); 318 assertEquals(tag, Address.pack(expect.getFrom()), actual.mFrom); 319 assertEquals(tag, expect.getSentDate().getTime(), actual.mTimeStamp); 320 assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.TO)), actual.mTo); 321 assertEquals(tag, Address.pack(expect.getRecipients(RecipientType.CC)), actual.mCc); 322 assertEquals(tag, ((MimeMessage)expect).getMessageId(), actual.mMessageId); 323 assertEquals(tag, expect.isSet(Flag.SEEN), actual.mFlagRead); 324 assertEquals(tag, expect.isSet(Flag.FLAGGED), actual.mFlagFavorite); 325 } 326 327 /** 328 * Check equality of a pair of converted messages 329 */ 330 private void checkLegacyMessage(String tag, EmailContent.Message expect, Message actual) 331 throws MessagingException { 332 assertEquals(tag, expect.mServerId, actual.getUid()); 333 assertEquals(tag, expect.mServerTimeStamp, actual.getInternalDate().getTime()); 334 assertEquals(tag, expect.mSubject, actual.getSubject()); 335 assertEquals(tag, expect.mFrom, Address.pack(actual.getFrom())); 336 assertEquals(tag, expect.mTimeStamp, actual.getSentDate().getTime()); 337 assertEquals(tag, expect.mTo, Address.pack(actual.getRecipients(RecipientType.TO))); 338 assertEquals(tag, expect.mCc, Address.pack(actual.getRecipients(RecipientType.CC))); 339 assertEquals(tag, expect.mBcc, Address.pack(actual.getRecipients(RecipientType.BCC))); 340 assertEquals(tag, expect.mReplyTo, Address.pack(actual.getReplyTo())); 341 assertEquals(tag, expect.mMessageId, ((MimeMessage)actual).getMessageId()); 342 // check flags 343 assertEquals(tag, expect.mFlagRead, actual.isSet(Flag.SEEN)); 344 assertEquals(tag, expect.mFlagFavorite, actual.isSet(Flag.FLAGGED)); 345 346 // Check the body of the message 347 ArrayList<Part> viewables = new ArrayList<Part>(); 348 ArrayList<Part> attachments = new ArrayList<Part>(); 349 MimeUtility.collectParts(actual, viewables, attachments); 350 String get1Text = null; 351 String get1Html = null; 352 String get1TextReply = null; 353 String get1HtmlReply = null; 354 String get1TextIntro = null; 355 for (Part viewable : viewables) { 356 String text = MimeUtility.getTextFromPart(viewable); 357 boolean isHtml = viewable.getMimeType().equalsIgnoreCase("text/html"); 358 String[] headers = viewable.getHeader(MimeHeader.HEADER_ANDROID_BODY_QUOTED_PART); 359 if (headers != null) { 360 String header = headers[0]; 361 boolean isReply = LegacyConversions.BODY_QUOTED_PART_REPLY.equalsIgnoreCase(header); 362 boolean isFwd = LegacyConversions.BODY_QUOTED_PART_FORWARD.equalsIgnoreCase(header); 363 boolean isIntro = LegacyConversions.BODY_QUOTED_PART_INTRO.equalsIgnoreCase(header); 364 if (isReply || isFwd) { 365 if (isHtml) { 366 get1HtmlReply = text; 367 } else { 368 get1TextReply = text; 369 } 370 } else if (isIntro) { 371 get1TextIntro = text; 372 } 373 // Check flags 374 int replyTypeFlags = expect.mFlags & EmailContent.Message.FLAG_TYPE_MASK; 375 if (isReply) { 376 assertEquals(tag, EmailContent.Message.FLAG_TYPE_REPLY, replyTypeFlags); 377 } 378 if (isFwd) { 379 assertEquals(tag, EmailContent.Message.FLAG_TYPE_FORWARD, replyTypeFlags); 380 } 381 } else { 382 if (isHtml) { 383 get1Html = text; 384 } else { 385 get1Text = text; 386 } 387 } 388 } 389 assertEquals(tag, expect.mText, get1Text); 390 assertEquals(tag, expect.mHtml, get1Html); 391 assertEquals(tag, expect.mTextReply, get1TextReply); 392 assertEquals(tag, expect.mHtmlReply, get1HtmlReply); 393 assertEquals(tag, expect.mIntroText, get1TextIntro); 394 395 // TODO Check the attachments 396 397// cv.put("attachment_count", attachments.size()); 398 } 399} 400