1 /* 2 * Copyright (C) 2008 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 17 package com.android.email.mail.store; 18 19 import com.android.email.Email; 20 import com.android.email.MockVendorPolicy; 21 import com.android.email.Utility; 22 import com.android.email.VendorPolicyLoader; 23 import com.android.email.mail.Address; 24 import com.android.email.mail.AuthenticationFailedException; 25 import com.android.email.mail.Body; 26 import com.android.email.mail.FetchProfile; 27 import com.android.email.mail.Flag; 28 import com.android.email.mail.Folder; 29 import com.android.email.mail.Folder.FolderType; 30 import com.android.email.mail.Folder.OpenMode; 31 import com.android.email.mail.Message; 32 import com.android.email.mail.Message.RecipientType; 33 import com.android.email.mail.MessagingException; 34 import com.android.email.mail.Part; 35 import com.android.email.mail.Transport; 36 import com.android.email.mail.internet.MimeBodyPart; 37 import com.android.email.mail.internet.MimeMultipart; 38 import com.android.email.mail.internet.MimeUtility; 39 import com.android.email.mail.internet.TextBody; 40 import com.android.email.mail.store.ImapStore.ImapConnection; 41 import com.android.email.mail.store.ImapStore.ImapMessage; 42 import com.android.email.mail.store.imap.ImapResponse; 43 import com.android.email.mail.store.imap.ImapTestUtils; 44 import com.android.email.mail.transport.MockTransport; 45 46 import org.apache.commons.io.IOUtils; 47 48 import android.os.Bundle; 49 import android.test.AndroidTestCase; 50 import android.test.MoreAsserts; 51 import android.test.suitebuilder.annotation.SmallTest; 52 53 import java.util.ArrayList; 54 import java.util.HashMap; 55 import java.util.regex.Pattern; 56 57 /** 58 * This is a series of unit tests for the ImapStore class. These tests must be locally 59 * complete - no server(s) required. 60 * 61 * To run these tests alone, use: 62 * $ runtest -c com.android.email.mail.store.ImapStoreUnitTests email 63 * 64 * TODO Check if callback is really called 65 * TODO test for BAD response in various places? 66 * TODO test for BYE response in various places? 67 */ 68 @SmallTest 69 public class ImapStoreUnitTests extends AndroidTestCase { 70 private final static String[] NO_REPLY = new String[0]; 71 72 /** 73 * Default folder name. In order to test for encoding, we use a non-ascii name. 74 */ 75 private final static String FOLDER_NAME = "\u65E5"; 76 77 /** 78 * Folder name encoded in UTF-7. 79 */ 80 private final static String FOLDER_ENCODED = "&ZeU-"; 81 82 private final static ImapResponse CAPABILITY_RESPONSE = ImapTestUtils.parseResponse( 83 "* CAPABILITY IMAP4rev1 STARTTLS"); 84 85 /* These values are provided by setUp() */ 86 private ImapStore mStore = null; 87 private ImapStore.ImapFolder mFolder = null; 88 89 private int mNextTag; 90 91 /** 92 * Setup code. We generate a lightweight ImapStore and ImapStore.ImapFolder. 93 */ 94 @Override 95 protected void setUp() throws Exception { 96 super.setUp(); 97 Email.setTempDirectory(getContext()); 98 99 // These are needed so we can get at the inner classes 100 mStore = (ImapStore) ImapStore.newInstance("imap://user:password@server:999", 101 getContext(), null); 102 mFolder = (ImapStore.ImapFolder) mStore.getFolder(FOLDER_NAME); 103 mNextTag = 1; 104 } 105 106 public void testJoinMessageUids() throws Exception { 107 assertEquals("", ImapStore.joinMessageUids(new Message[] {})); 108 assertEquals("a", ImapStore.joinMessageUids(new Message[] { 109 mFolder.createMessage("a") 110 })); 111 assertEquals("a,XX", ImapStore.joinMessageUids(new Message[] { 112 mFolder.createMessage("a"), 113 mFolder.createMessage("XX"), 114 })); 115 } 116 117 /** 118 * Confirms simple non-SSL non-TLS login 119 */ 120 public void testSimpleLogin() throws MessagingException { 121 122 MockTransport mockTransport = openAndInjectMockTransport(); 123 124 // try to open it 125 setupOpenFolder(mockTransport); 126 mFolder.open(OpenMode.READ_WRITE, null); 127 128 // TODO: inject specific facts in the initial folder SELECT and check them here 129 } 130 131 /** 132 * Test simple login with failed authentication 133 */ 134 public void testLoginFailure() throws Exception { 135 MockTransport mockTransport = openAndInjectMockTransport(); 136 expectLogin(mockTransport, false, false, new String[] {"* iD nIL", "oK"}, 137 "nO authentication failed"); 138 139 try { 140 mStore.getConnection().open(); 141 fail("Didn't throw AuthenticationFailedException"); 142 } catch (AuthenticationFailedException expected) { 143 } 144 } 145 146 /** 147 * Test simple TLS open 148 */ 149 public void testTlsOpen() throws MessagingException { 150 151 MockTransport mockTransport = openAndInjectMockTransport(Transport.CONNECTION_SECURITY_TLS, 152 false); 153 154 // try to open it, with STARTTLS 155 expectLogin(mockTransport, true, false, 156 new String[] {"* iD nIL", "oK"}, "oK user authenticated (Success)"); 157 mockTransport.expect( 158 getNextTag(false) + " SELECT \"" + FOLDER_ENCODED + "\"", new String[] { 159 "* fLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)", 160 "* oK [pERMANENTFLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen \\*)]", 161 "* 0 eXISTS", 162 "* 0 rECENT", 163 "* OK [uNSEEN 0]", 164 "* OK [uIDNEXT 1]", 165 getNextTag(true) + " oK [" + "rEAD-wRITE" + "] " + 166 FOLDER_ENCODED + " selected. (Success)"}); 167 168 mFolder.open(OpenMode.READ_WRITE, null); 169 assertTrue(mockTransport.isTlsStarted()); 170 } 171 172 /** 173 * TODO: Test with SSL negotiation (faked) 174 * TODO: Test with SSL required but not supported 175 * TODO: Test with TLS required but not supported 176 */ 177 178 /** 179 * Test the generation of the IMAP ID keys 180 */ 181 public void testImapIdBasic() { 182 // First test looks at operation of the outer API - we don't control any of the 183 // values; Just look for basic results. 184 185 // Strings we'll expect to find: 186 // name Android package name of the program 187 // os "android" 188 // os-version "version; build-id" 189 // vendor Vendor of the client/server 190 // x-android-device-model Model (Optional, so not tested here) 191 // x-android-net-operator Carrier (Unreliable, so not tested here) 192 // AGUID A device+account UID 193 String id = ImapStore.getImapId(getContext(), "user-name", "host-name", 194 CAPABILITY_RESPONSE); 195 HashMap<String, String> map = tokenizeImapId(id); 196 assertEquals(getContext().getPackageName(), map.get("name")); 197 assertEquals("android", map.get("os")); 198 assertNotNull(map.get("os-version")); 199 assertNotNull(map.get("vendor")); 200 assertNotNull(map.get("AGUID")); 201 202 // Next, use the inner API to confirm operation of a couple of 203 // variants for release and non-release devices. 204 205 // simple API check - non-REL codename, non-empty version 206 id = ImapStore.makeCommonImapId("packageName", "version", "codeName", 207 "model", "id", "vendor", "network-operator"); 208 map = tokenizeImapId(id); 209 assertEquals("packageName", map.get("name")); 210 assertEquals("android", map.get("os")); 211 assertEquals("version; id", map.get("os-version")); 212 assertEquals("vendor", map.get("vendor")); 213 assertEquals(null, map.get("x-android-device-model")); 214 assertEquals("network-operator", map.get("x-android-mobile-net-operator")); 215 assertEquals(null, map.get("AGUID")); 216 217 // simple API check - codename is REL, so use model name. 218 // also test empty version => 1.0 and empty network operator 219 id = ImapStore.makeCommonImapId("packageName", "", "REL", 220 "model", "id", "vendor", ""); 221 map = tokenizeImapId(id); 222 assertEquals("packageName", map.get("name")); 223 assertEquals("android", map.get("os")); 224 assertEquals("1.0; id", map.get("os-version")); 225 assertEquals("vendor", map.get("vendor")); 226 assertEquals("model", map.get("x-android-device-model")); 227 assertEquals(null, map.get("x-android-mobile-net-operator")); 228 assertEquals(null, map.get("AGUID")); 229 } 230 231 /** 232 * Test for the interaction between {@link ImapStore#getImapId} and a vendor policy. 233 */ 234 public void testImapIdWithVendorPolicy() { 235 try { 236 MockVendorPolicy.inject(getContext()); 237 238 // Prepare mock result 239 Bundle result = new Bundle(); 240 result.putString("getImapId", "\"test-key\" \"test-value\""); 241 MockVendorPolicy.mockResult = result; 242 243 // Invoke 244 String id = ImapStore.getImapId(getContext(), "user-name", "host-name", 245 ImapTestUtils.parseResponse("* CAPABILITY IMAP4rev1 XXX YYY Z")); 246 247 // Check the result 248 assertEquals("test-value", tokenizeImapId(id).get("test-key")); 249 250 // Verify what's passed to the policy 251 assertEquals("getImapId", MockVendorPolicy.passedPolicy); 252 assertEquals("user-name", MockVendorPolicy.passedBundle.getString("getImapId.user")); 253 assertEquals("host-name", MockVendorPolicy.passedBundle.getString("getImapId.host")); 254 assertEquals("[CAPABILITY,IMAP4rev1,XXX,YYY,Z]", 255 MockVendorPolicy.passedBundle.getString("getImapId.capabilities")); 256 } finally { 257 VendorPolicyLoader.clearInstanceForTest(); 258 } 259 } 260 261 /** 262 * Test of the internal generator for IMAP ID strings, specifically looking for proper 263 * filtering of illegal values. This is required because we cannot necessarily trust 264 * the external sources of some of this data (e.g. release labels). 265 * 266 * The (somewhat arbitrary) legal values are: a-z A-Z 0-9 - _ + = ; : . , / <space> 267 * The most important goal of the filters is to keep out control chars, (, ), and " 268 */ 269 public void testImapIdFiltering() { 270 String id = ImapStore.makeCommonImapId( 271 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 272 "0123456789", "codeName", 273 "model", "-_+=;:.,// ", 274 "v(e)n\"d\ro\nr", // look for bad chars stripped out, leaving OK chars 275 "()\""); // look for bad chars stripped out, leaving nothing 276 HashMap<String, String> map = tokenizeImapId(id); 277 278 assertEquals("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", map.get("name")); 279 assertEquals("0123456789; -_+=;:.,// ", map.get("os-version")); 280 assertEquals("vendor", map.get("vendor")); 281 assertNull(map.get("x-android-mobile-net-operator")); 282 } 283 284 /** 285 * Test that IMAP ID uid's are per-username 286 */ 287 public void testImapIdDeviceId() throws MessagingException { 288 ImapStore store1a = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999", 289 getContext(), null); 290 ImapStore store1b = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999", 291 getContext(), null); 292 ImapStore store2 = (ImapStore) ImapStore.newInstance("imap://user2:password@server:999", 293 getContext(), null); 294 295 String id1a = ImapStore.getImapId(getContext(), "user1", "host-name", CAPABILITY_RESPONSE); 296 String id1b = ImapStore.getImapId(getContext(), "user1", "host-name", CAPABILITY_RESPONSE); 297 String id2 = ImapStore.getImapId(getContext(), "user2", "host-name", CAPABILITY_RESPONSE); 298 299 String uid1a = tokenizeImapId(id1a).get("AGUID"); 300 String uid1b = tokenizeImapId(id1b).get("AGUID"); 301 String uid2 = tokenizeImapId(id2).get("AGUID"); 302 303 assertEquals(uid1a, uid1b); 304 MoreAsserts.assertNotEqual(uid1a, uid2); 305 } 306 307 /** 308 * Helper to break an IMAP ID string into keys & values 309 * @param id the IMAP Id string (the part inside the parens) 310 * @return a map of key/value pairs 311 */ 312 private HashMap<String, String> tokenizeImapId(String id) { 313 // Instead of a true tokenizer, we'll use double-quote as the split. 314 // We can's use " " because there may be spaces inside the values. 315 String[] elements = id.split("\""); 316 HashMap<String, String> map = new HashMap<String, String>(); 317 for (int i = 0; i < elements.length; ) { 318 // Because we split at quotes, we expect to find: 319 // [i] = null or one or more spaces 320 // [i+1] = key 321 // [i+2] = one or more spaces 322 // [i+3] = value 323 map.put(elements[i+1], elements[i+3]); 324 i += 4; 325 } 326 return map; 327 } 328 329 /** 330 * Test non-NIL server response to IMAP ID. We should simply ignore it. 331 */ 332 public void testServerId() throws MessagingException { 333 MockTransport mockTransport = openAndInjectMockTransport(); 334 335 // try to open it 336 setupOpenFolder(mockTransport, new String[] { 337 "* ID (\"name\" \"Cyrus\" \"version\" \"1.5\"" + 338 " \"os\" \"sunos\" \"os-version\" \"5.5\"" + 339 " \"support-url\" \"mailto:cyrus-bugs+@andrew.cmu.edu\")", 340 "oK"}, "rEAD-wRITE"); 341 mFolder.open(OpenMode.READ_WRITE, null); 342 } 343 344 /** 345 * Test OK response to IMAP ID with crummy text afterwards too. 346 */ 347 public void testImapIdOkParsing() throws MessagingException { 348 MockTransport mockTransport = openAndInjectMockTransport(); 349 350 // try to open it 351 setupOpenFolder(mockTransport, new String[] { 352 "* iD nIL", 353 "oK [iD] bad-char-%"}, "rEAD-wRITE"); 354 mFolder.open(OpenMode.READ_WRITE, null); 355 } 356 357 /** 358 * Test BAD response to IMAP ID - also with bad parser chars 359 */ 360 public void testImapIdBad() throws MessagingException { 361 MockTransport mockTransport = openAndInjectMockTransport(); 362 363 // try to open it 364 setupOpenFolder(mockTransport, new String[] { 365 "bAD unknown command bad-char-%"}, "rEAD-wRITE"); 366 mFolder.open(OpenMode.READ_WRITE, null); 367 } 368 369 /** 370 * Confirm that when IMAP ID is not in capability, it is not sent/received. 371 * This supports RFC 2971 section 3, and is important because certain servers 372 * (e.g. imap.vodafone.net.nz) do not process the unexpected ID command properly. 373 */ 374 public void testImapIdNotSupported() throws MessagingException { 375 MockTransport mockTransport = openAndInjectMockTransport(); 376 377 // try to open it 378 setupOpenFolder(mockTransport, null, "rEAD-wRITE"); 379 mFolder.open(OpenMode.READ_WRITE, null); 380 } 381 382 /** 383 * Test small Folder functions that don't really do anything in Imap 384 */ 385 public void testSmallFolderFunctions() throws MessagingException { 386 // getPermanentFlags() returns { Flag.DELETED, Flag.SEEN, Flag.FLAGGED } 387 Flag[] flags = mFolder.getPermanentFlags(); 388 assertEquals(3, flags.length); 389 // TODO: Write flags into hashset and compare them to a hashset and compare them 390 assertEquals(Flag.DELETED, flags[0]); 391 assertEquals(Flag.SEEN, flags[1]); 392 assertEquals(Flag.FLAGGED, flags[2]); 393 394 // canCreate() returns true 395 assertTrue(mFolder.canCreate(FolderType.HOLDS_FOLDERS)); 396 assertTrue(mFolder.canCreate(FolderType.HOLDS_MESSAGES)); 397 } 398 399 /** 400 * Lightweight test to confirm that IMAP hasn't implemented any folder roles yet. 401 * 402 * TODO: Test this with multiple folders provided by mock server 403 * TODO: Implement XLIST and then support this 404 */ 405 public void testNoFolderRolesYet() { 406 assertEquals(Folder.FolderRole.UNKNOWN, mFolder.getRole()); 407 } 408 409 /** 410 * Lightweight test to confirm that IMAP isn't requesting structure prefetch. 411 */ 412 public void testNoStructurePrefetch() { 413 assertFalse(mStore.requireStructurePrefetch()); 414 } 415 416 /** 417 * Lightweight test to confirm that IMAP is requesting sent-message-upload. 418 * TODO: Implement Gmail-specific cases and handle this server-side 419 */ 420 public void testSentUploadRequested() { 421 assertTrue(mStore.requireCopyMessageToSentFolder()); 422 } 423 424 /** 425 * TODO: Test the process of opening and indexing a mailbox with one unread message in it. 426 */ 427 428 /** 429 * TODO: Test the scenario where the transport is "open" but not really (e.g. server closed). 430 /** 431 * Set up a basic MockTransport. open it, and inject it into mStore 432 */ 433 private MockTransport openAndInjectMockTransport() { 434 return openAndInjectMockTransport(Transport.CONNECTION_SECURITY_NONE, false); 435 } 436 437 /** 438 * Set up a MockTransport with security settings 439 */ 440 private MockTransport openAndInjectMockTransport(int connectionSecurity, 441 boolean trustAllCertificates) { 442 // Create mock transport and inject it into the ImapStore that's already set up 443 MockTransport mockTransport = new MockTransport(); 444 mockTransport.setSecurity(connectionSecurity, trustAllCertificates); 445 mockTransport.setMockHost("mock.server.com"); 446 mStore.setTransport(mockTransport); 447 return mockTransport; 448 } 449 450 /** 451 * Helper which stuffs the mock with enough strings to satisfy a call to ImapFolder.open() 452 * 453 * @param mockTransport the mock transport we're using 454 */ 455 private void setupOpenFolder(MockTransport mockTransport) { 456 setupOpenFolder(mockTransport, "rEAD-wRITE"); 457 } 458 459 /** 460 * Helper which stuffs the mock with enough strings to satisfy a call to ImapFolder.open() 461 * 462 * @param mockTransport the mock transport we're using 463 */ 464 private void setupOpenFolder(MockTransport mockTransport, String readWriteMode) { 465 setupOpenFolder(mockTransport, new String[] { 466 "* iD nIL", "oK"}, readWriteMode); 467 } 468 469 /** 470 * Helper which stuffs the mock with enough strings to satisfy a call to ImapFolder.open() 471 * Also allows setting a custom IMAP ID. 472 * 473 * Also sets mNextTag, an int, which is useful if there are additional commands to inject. 474 * 475 * @param mockTransport the mock transport we're using 476 * @param imapIdResponse the expected series of responses to the IMAP ID command. Non-final 477 * lines should be tagged with *. The final response should be untagged (the correct 478 * tag will be added at runtime). Pass "null" to test w/o IMAP ID. 479 * @param readWriteMode "READ-WRITE" or "READ-ONLY" 480 */ 481 private void setupOpenFolder(MockTransport mockTransport, String[] imapIdResponse, 482 String readWriteMode) { 483 expectLogin(mockTransport, imapIdResponse); 484 mockTransport.expect( 485 getNextTag(false) + " SELECT \"" + FOLDER_ENCODED + "\"", new String[] { 486 "* fLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)", 487 "* oK [pERMANENTFLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen \\*)]", 488 "* 0 eXISTS", 489 "* 0 rECENT", 490 "* OK [uNSEEN 0]", 491 "* OK [uIDNEXT 1]", 492 getNextTag(true) + " oK [" + readWriteMode + "] " + 493 FOLDER_ENCODED + " selected. (Success)"}); 494 } 495 496 private void expectLogin(MockTransport mockTransport) { 497 expectLogin(mockTransport, new String[] {"* iD nIL", "oK"}); 498 } 499 500 private void expectLogin(MockTransport mockTransport, String[] imapIdResponse) { 501 expectLogin(mockTransport, false, (imapIdResponse != null), imapIdResponse, 502 "oK user authenticated (Success)"); 503 } 504 505 private void expectLogin(MockTransport mockTransport, boolean startTls, boolean withId, 506 String[] imapIdResponse, String loginResponse) { 507 // inject boilerplate commands that match our typical login 508 mockTransport.expect(null, "* oK Imap 2000 Ready To Assist You"); 509 510 expectCapability(mockTransport, withId); 511 512 // TLS (if expected) 513 if (startTls) { 514 mockTransport.expect(getNextTag(false) + " STARTTLS", 515 getNextTag(true) + " Ok starting TLS"); 516 mockTransport.expectStartTls(); 517 // After switching to TLS the client must re-query for capability 518 expectCapability(mockTransport, withId); 519 } 520 521 // ID 522 if (withId) { 523 String expectedNextTag = getNextTag(false); 524 // Fix the tag # of the ID response 525 String last = imapIdResponse[imapIdResponse.length-1]; 526 last = expectedNextTag + " " + last; 527 imapIdResponse[imapIdResponse.length-1] = last; 528 mockTransport.expect(getNextTag(false) + " ID \\(.*\\)", imapIdResponse); 529 getNextTag(true); // Advance the tag for ID response. 530 } 531 532 // LOGIN 533 mockTransport.expect(getNextTag(false) + " LOGIN user \"password\"", 534 getNextTag(true) + " " + loginResponse); 535 } 536 537 private void expectCapability(MockTransport mockTransport, boolean withId) { 538 String capabilityList = withId 539 ? "* cAPABILITY iMAP4rev1 sTARTTLS aUTH=gSSAPI lOGINDISABLED iD" 540 : "* cAPABILITY iMAP4rev1 sTARTTLS aUTH=gSSAPI lOGINDISABLED"; 541 542 mockTransport.expect(getNextTag(false) + " CAPABILITY", new String[] { 543 capabilityList, 544 getNextTag(true) + " oK CAPABILITY completed"}); 545 } 546 547 private void expectNoop(MockTransport mockTransport, boolean ok) { 548 String response = ok ? " oK success" : " nO timeout"; 549 mockTransport.expect(getNextTag(false) + " NOOP", 550 new String[] {getNextTag(true) + response}); 551 } 552 553 /** 554 * Return a tag for use in setting up expect strings. Typically this is called in pairs, 555 * first as getNextTag(false) when emitting the command, then as getNextTag(true) when 556 * emitting the final line of the expected response. 557 * @param advance true to increment mNextTag for the subsequence command 558 * @return a string containing the current tag 559 */ 560 public String getNextTag(boolean advance) { 561 if (advance) ++mNextTag; 562 return Integer.toString(mNextTag); 563 } 564 565 /** 566 * Test that servers reporting READ-WRITE mode are parsed properly 567 * Note: the READ_WRITE mode passed to folder.open() does not affect the test 568 */ 569 public void testReadWrite() throws MessagingException { 570 MockTransport mock = openAndInjectMockTransport(); 571 setupOpenFolder(mock, "rEAD-WRITE"); 572 mFolder.open(OpenMode.READ_WRITE, null); 573 assertEquals(OpenMode.READ_WRITE, mFolder.getMode()); 574 } 575 576 /** 577 * Test that servers reporting READ-ONLY mode are parsed properly 578 * Note: the READ_ONLY mode passed to folder.open() does not affect the test 579 */ 580 public void testReadOnly() throws MessagingException { 581 MockTransport mock = openAndInjectMockTransport(); 582 setupOpenFolder(mock, "rEAD-ONLY"); 583 mFolder.open(OpenMode.READ_ONLY, null); 584 assertEquals(OpenMode.READ_ONLY, mFolder.getMode()); 585 } 586 587 /** 588 * Test for getUnreadMessageCount with quoted string in the middle of response. 589 */ 590 public void testGetUnreadMessageCountWithQuotedString() throws Exception { 591 MockTransport mock = openAndInjectMockTransport(); 592 setupOpenFolder(mock); 593 mock.expect( 594 getNextTag(false) + " STATUS \"" + FOLDER_ENCODED + "\" \\(UNSEEN\\)", 595 new String[] { 596 "* sTATUS \"" + FOLDER_ENCODED + "\" (uNSEEN 2)", 597 getNextTag(true) + " oK STATUS completed"}); 598 mFolder.open(OpenMode.READ_WRITE, null); 599 int unreadCount = mFolder.getUnreadMessageCount(); 600 assertEquals("getUnreadMessageCount with quoted string", 2, unreadCount); 601 } 602 603 /** 604 * Test for getUnreadMessageCount with literal string in the middle of response. 605 */ 606 public void testGetUnreadMessageCountWithLiteralString() throws Exception { 607 MockTransport mock = openAndInjectMockTransport(); 608 setupOpenFolder(mock); 609 mock.expect( 610 getNextTag(false) + " STATUS \"" + FOLDER_ENCODED + "\" \\(UNSEEN\\)", 611 new String[] { 612 "* sTATUS {5}", 613 FOLDER_ENCODED + " (uNSEEN 10)", 614 getNextTag(true) + " oK STATUS completed"}); 615 mFolder.open(OpenMode.READ_WRITE, null); 616 int unreadCount = mFolder.getUnreadMessageCount(); 617 assertEquals("getUnreadMessageCount with literal string", 10, unreadCount); 618 } 619 620 public void testFetchFlagEnvelope() throws MessagingException { 621 final MockTransport mock = openAndInjectMockTransport(); 622 setupOpenFolder(mock); 623 mFolder.open(OpenMode.READ_WRITE, null); 624 final Message message = mFolder.createMessage("1"); 625 626 final FetchProfile fp = new FetchProfile(); 627 fp.add(FetchProfile.Item.FLAGS); 628 fp.add(FetchProfile.Item.ENVELOPE); 629 mock.expect(getNextTag(false) + 630 " UID FETCH 1 \\(UID FLAGS INTERNALDATE RFC822\\.SIZE BODY\\.PEEK\\[HEADER.FIELDS" + 631 " \\(date subject from content-type to cc message-id\\)\\]\\)", 632 new String[] { 633 "* 9 fETCH (uID 1 rFC822.sIZE 120626 iNTERNALDATE \"17-may-2010 22:00:15 +0000\"" + 634 "fLAGS (\\Seen) bODY[hEADER.FIELDS (dAte sUbject fRom cOntent-type tO cC" + 635 " mEssage-id)]" + 636 " {279}", 637 "From: Xxxxxx Yyyyy <userxx (at) android.com>", 638 "Date: Mon, 17 May 2010 14:59:52 -0700", 639 "Message-ID: <x0000000000000000000000000000000000000000000000y (at) android.com>", 640 "Subject: ssubject", 641 "To: android.test01 (at) android.com", 642 "Content-Type: multipart/mixed; boundary=a00000000000000000000000000b", 643 "", 644 ")", 645 getNextTag(true) + " oK SUCCESS" 646 }); 647 mFolder.fetch(new Message[] { message }, fp, null); 648 649 assertEquals("android.test01 (at) android.com", message.getHeader("to")[0]); 650 assertEquals("Xxxxxx Yyyyy <userxx (at) android.com>", message.getHeader("from")[0]); 651 assertEquals("multipart/mixed; boundary=a00000000000000000000000000b", 652 message.getHeader("Content-Type")[0]); 653 assertTrue(message.isSet(Flag.SEEN)); 654 655 // TODO: Test NO response. 656 } 657 658 /** 659 * Test for fetching simple BODYSTRUCTURE. 660 */ 661 public void testFetchBodyStructureSimple() throws Exception { 662 final MockTransport mock = openAndInjectMockTransport(); 663 setupOpenFolder(mock); 664 mFolder.open(OpenMode.READ_WRITE, null); 665 final Message message = mFolder.createMessage("1"); 666 667 final FetchProfile fp = new FetchProfile(); 668 fp.add(FetchProfile.Item.STRUCTURE); 669 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)", 670 new String[] { 671 "* 9 fETCH (uID 1 bODYSTRUCTURE (\"tEXT\" \"pLAIN\" nIL" + 672 " nIL nIL nIL 18 3 nIL nIL nIL))", 673 getNextTag(true) + " oK sUCCESS" 674 }); 675 mFolder.fetch(new Message[] { message }, fp, null); 676 677 // Check mime structure... 678 MoreAsserts.assertEquals( 679 new String[] {"text/plain"}, 680 message.getHeader("Content-Type") 681 ); 682 assertNull(message.getHeader("Content-Transfer-Encoding")); 683 assertNull(message.getHeader("Content-ID")); 684 MoreAsserts.assertEquals( 685 new String[] {";\n size=18"}, 686 message.getHeader("Content-Disposition") 687 ); 688 689 MoreAsserts.assertEquals( 690 new String[] {"TEXT"}, 691 message.getHeader("X-Android-Attachment-StoreData") 692 ); 693 694 // TODO: Test NO response. 695 } 696 697 /** 698 * Test for fetching complex muiltipart BODYSTRUCTURE. 699 */ 700 public void testFetchBodyStructureMultipart() throws Exception { 701 final MockTransport mock = openAndInjectMockTransport(); 702 setupOpenFolder(mock); 703 mFolder.open(OpenMode.READ_WRITE, null); 704 final Message message = mFolder.createMessage("1"); 705 706 final FetchProfile fp = new FetchProfile(); 707 fp.add(FetchProfile.Item.STRUCTURE); 708 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)", 709 new String[] { 710 "* 9 fETCH (uID 1 bODYSTRUCTURE ((\"tEXT\" \"pLAIN\" () {20}", 711 "long content id#@!@#" + 712 " NIL \"7BIT\" 18 3 NIL NIL NIL)" + 713 "(\"IMAGE\" \"PNG\" (\"NAME\" {10}", 714 "device.png) NIL NIL \"BASE64\" {6}", 715 "117840 NIL (\"aTTACHMENT\" (\"fILENAME\" \"device.png\")) NIL)" + 716 "(\"TEXT\" \"HTML\" () NIL NIL \"7BIT\" 100 NIL 123 (\"aTTACHMENT\"" + 717 "(\"fILENAME\" {15}", 718 "attachment.html \"SIZE\" 555)) NIL)" + 719 "((\"TEXT\" \"HTML\" NIL NIL \"BASE64\")(\"XXX\" \"YYY\"))" + // Nested 720 "\"mIXED\" (\"bOUNDARY\" \"00032556278a7005e40486d159ca\") NIL NIL))", 721 getNextTag(true) + " oK SUCCESS" 722 }); 723 mFolder.fetch(new Message[] { message }, fp, null); 724 725 // Check mime structure... 726 final Body body = message.getBody(); 727 assertTrue(body instanceof MimeMultipart); 728 MimeMultipart mimeMultipart = (MimeMultipart) body; 729 assertEquals(4, mimeMultipart.getCount()); 730 assertEquals("mixed", mimeMultipart.getSubTypeForTest()); 731 732 final Part part1 = mimeMultipart.getBodyPart(0); 733 final Part part2 = mimeMultipart.getBodyPart(1); 734 final Part part3 = mimeMultipart.getBodyPart(2); 735 final Part part4 = mimeMultipart.getBodyPart(3); 736 assertTrue(part1 instanceof MimeBodyPart); 737 assertTrue(part2 instanceof MimeBodyPart); 738 assertTrue(part3 instanceof MimeBodyPart); 739 assertTrue(part4 instanceof MimeBodyPart); 740 741 final MimeBodyPart mimePart1 = (MimeBodyPart) part1; // text/plain 742 final MimeBodyPart mimePart2 = (MimeBodyPart) part2; // image/png 743 final MimeBodyPart mimePart3 = (MimeBodyPart) part3; // text/html 744 final MimeBodyPart mimePart4 = (MimeBodyPart) part4; // Nested 745 746 MoreAsserts.assertEquals( 747 new String[] {"1"}, 748 part1.getHeader("X-Android-Attachment-StoreData") 749 ); 750 MoreAsserts.assertEquals( 751 new String[] {"2"}, 752 part2.getHeader("X-Android-Attachment-StoreData") 753 ); 754 MoreAsserts.assertEquals( 755 new String[] {"3"}, 756 part3.getHeader("X-Android-Attachment-StoreData") 757 ); 758 759 MoreAsserts.assertEquals( 760 new String[] {"text/plain"}, 761 part1.getHeader("Content-Type") 762 ); 763 MoreAsserts.assertEquals( 764 new String[] {"image/png;\n NAME=\"device.png\""}, 765 part2.getHeader("Content-Type") 766 ); 767 MoreAsserts.assertEquals( 768 new String[] {"text/html"}, 769 part3.getHeader("Content-Type") 770 ); 771 772 MoreAsserts.assertEquals( 773 new String[] {"long content id#@!@#"}, 774 part1.getHeader("Content-ID") 775 ); 776 assertNull(part2.getHeader("Content-ID")); 777 assertNull(part3.getHeader("Content-ID")); 778 779 MoreAsserts.assertEquals( 780 new String[] {"7BIT"}, 781 part1.getHeader("Content-Transfer-Encoding") 782 ); 783 MoreAsserts.assertEquals( 784 new String[] {"BASE64"}, 785 part2.getHeader("Content-Transfer-Encoding") 786 ); 787 MoreAsserts.assertEquals( 788 new String[] {"7BIT"}, 789 part3.getHeader("Content-Transfer-Encoding") 790 ); 791 792 MoreAsserts.assertEquals( 793 new String[] {";\n size=18"}, 794 part1.getHeader("Content-Disposition") 795 ); 796 MoreAsserts.assertEquals( 797 new String[] {"attachment;\n filename=\"device.png\";\n size=117840"}, 798 part2.getHeader("Content-Disposition") 799 ); 800 MoreAsserts.assertEquals( 801 new String[] {"attachment;\n filename=\"attachment.html\";\n size=\"555\""}, 802 part3.getHeader("Content-Disposition") 803 ); 804 805 // Check the nested parts. 806 final Body part4body = part4.getBody(); 807 assertTrue(part4body instanceof MimeMultipart); 808 MimeMultipart mimeMultipartPart4 = (MimeMultipart) part4body; 809 assertEquals(2, mimeMultipartPart4.getCount()); 810 811 final MimeBodyPart mimePart41 = (MimeBodyPart) mimeMultipartPart4.getBodyPart(0); 812 final MimeBodyPart mimePart42 = (MimeBodyPart) mimeMultipartPart4.getBodyPart(1); 813 814 MoreAsserts.assertEquals(new String[] {"4.1"}, 815 mimePart41.getHeader("X-Android-Attachment-StoreData") 816 ); 817 MoreAsserts.assertEquals(new String[] {"4.2"}, 818 mimePart42.getHeader("X-Android-Attachment-StoreData") 819 ); 820 } 821 822 public void testFetchBodySane() throws MessagingException { 823 final MockTransport mock = openAndInjectMockTransport(); 824 setupOpenFolder(mock); 825 mFolder.open(OpenMode.READ_WRITE, null); 826 final Message message = mFolder.createMessage("1"); 827 828 final FetchProfile fp = new FetchProfile(); 829 fp.add(FetchProfile.Item.BODY_SANE); 830 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[\\]<0.51200>\\)", 831 new String[] { 832 "* 9 fETCH (uID 1 bODY[] {23}", 833 "from: a (at) b.com", // 15 bytes 834 "", // 2 835 "test", // 6 836 ")", 837 getNextTag(true) + " oK SUCCESS" 838 }); 839 mFolder.fetch(new Message[] { message }, fp, null); 840 assertEquals("a (at) b.com", message.getHeader("from")[0]); 841 842 // TODO: Test NO response. 843 } 844 845 public void testFetchBody() throws MessagingException { 846 final MockTransport mock = openAndInjectMockTransport(); 847 setupOpenFolder(mock); 848 mFolder.open(OpenMode.READ_WRITE, null); 849 final Message message = mFolder.createMessage("1"); 850 851 final FetchProfile fp = new FetchProfile(); 852 fp.add(FetchProfile.Item.BODY); 853 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[\\]\\)", 854 new String[] { 855 "* 9 fETCH (uID 1 bODY[] {23}", 856 "from: a (at) b.com", // 15 bytes 857 "", // 2 858 "test", // 6 859 ")", 860 getNextTag(true) + " oK SUCCESS" 861 }); 862 mFolder.fetch(new Message[] { message }, fp, null); 863 assertEquals("a (at) b.com", message.getHeader("from")[0]); 864 865 // TODO: Test NO response. 866 } 867 868 public void testFetchAttachment() throws Exception { 869 MockTransport mock = openAndInjectMockTransport(); 870 setupOpenFolder(mock); 871 mFolder.open(OpenMode.READ_WRITE, null); 872 final Message message = mFolder.createMessage("1"); 873 874 final FetchProfile fp = new FetchProfile(); 875 fp.add(FetchProfile.Item.STRUCTURE); 876 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)", 877 new String[] { 878 "* 9 fETCH (uID 1 bODYSTRUCTURE ((\"tEXT\" \"PLAIN\" (\"cHARSET\" \"iSO-8859-1\")" + 879 " CID nIL \"7bIT\" 18 3 NIL NIL NIL)" + 880 "(\"IMAGE\" \"PNG\"" + 881 " (\"nAME\" \"device.png\") NIL NIL \"bASE64\" 117840 NIL (\"aTTACHMENT\"" + 882 "(\"fILENAME\" \"device.png\")) NIL)" + 883 "\"mIXED\"))", 884 getNextTag(true) + " OK SUCCESS" 885 }); 886 mFolder.fetch(new Message[] { message }, fp, null); 887 888 // Check mime structure, and get the second part. 889 Body body = message.getBody(); 890 assertTrue(body instanceof MimeMultipart); 891 MimeMultipart mimeMultipart = (MimeMultipart) body; 892 assertEquals(2, mimeMultipart.getCount()); 893 894 Part part1 = mimeMultipart.getBodyPart(1); 895 assertTrue(part1 instanceof MimeBodyPart); 896 MimeBodyPart mimePart1 = (MimeBodyPart) part1; 897 898 // Fetch the second part 899 fp.clear(); 900 fp.add(mimePart1); 901 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[2\\]\\)", 902 new String[] { 903 "* 9 fETCH (uID 1 bODY[2] {4}", 904 "YWJj)", // abc in base64 905 getNextTag(true) + " oK SUCCESS" 906 }); 907 mFolder.fetch(new Message[] { message }, fp, null); 908 909 assertEquals("abc", 910 Utility.fromUtf8(IOUtils.toByteArray(mimePart1.getBody().getInputStream()))); 911 912 // TODO: Test NO response. 913 } 914 915 /** 916 * Test for proper operations on servers that return "NIL" for empty message bodies. 917 */ 918 public void testNilMessage() throws MessagingException { 919 MockTransport mock = openAndInjectMockTransport(); 920 setupOpenFolder(mock); 921 mFolder.open(OpenMode.READ_WRITE, null); 922 923 // Prepare to pull structure and peek body text - this is like the "large message" 924 // loop in MessagingController.synchronizeMailboxGeneric() 925 FetchProfile fp = new FetchProfile();fp.clear(); 926 fp.add(FetchProfile.Item.STRUCTURE); 927 Message message1 = mFolder.createMessage("1"); 928 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)", new String[] { 929 "* 1 fETCH (uID 1 bODYSTRUCTURE (tEXT pLAIN nIL nIL nIL 7bIT 0 0 nIL nIL nIL))", 930 getNextTag(true) + " oK SUCCESS" 931 }); 932 mFolder.fetch(new Message[] { message1 }, fp, null); 933 934 // The expected result for an empty body is: 935 // * 1 FETCH (UID 1 BODY[TEXT] {0}) 936 // But some servers are returning NIL for the empty body: 937 // * 1 FETCH (UID 1 BODY[TEXT] NIL) 938 // Because this breaks our little parser, fetch() skips over empty parts. 939 // The rest of this test is confirming that this is the case. 940 941 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[TEXT\\]\\)", new String[] { 942 "* 1 fETCH (uID 1 bODY[tEXT] nIL)", 943 getNextTag(true) + " oK SUCCESS" 944 }); 945 ArrayList<Part> viewables = new ArrayList<Part>(); 946 ArrayList<Part> attachments = new ArrayList<Part>(); 947 MimeUtility.collectParts(message1, viewables, attachments); 948 assertTrue(viewables.size() == 1); 949 Part emptyBodyPart = viewables.get(0); 950 fp.clear(); 951 fp.add(emptyBodyPart); 952 mFolder.fetch(new Message[] { message1 }, fp, null); 953 954 // If this wasn't working properly, there would be an attempted interpretation 955 // of the empty part's NIL and possibly a crash. 956 957 // If this worked properly, the "empty" body can now be retrieved 958 viewables = new ArrayList<Part>(); 959 attachments = new ArrayList<Part>(); 960 MimeUtility.collectParts(message1, viewables, attachments); 961 assertTrue(viewables.size() == 1); 962 emptyBodyPart = viewables.get(0); 963 String text = MimeUtility.getTextFromPart(emptyBodyPart); 964 assertNull(text); 965 } 966 967 /** 968 * Confirm the IMAP parser won't crash when seeing an excess FETCH response line without UID. 969 * 970 * <p>We've observed that the secure.emailsrvr.com email server returns an excess FETCH response 971 * for a UID FETCH command. These excess responses doesn't have the UID field in it, even 972 * though we request, which led the response parser to crash. We fixed it by ignoring response 973 * lines that don't have UID. This test is to make sure this case. 974 */ 975 public void testExcessFetchResult() throws MessagingException { 976 MockTransport mock = openAndInjectMockTransport(); 977 setupOpenFolder(mock); 978 mFolder.open(OpenMode.READ_WRITE, null); 979 980 // Create a message, and make sure it's not "SEEN". 981 Message message1 = mFolder.createMessage("1"); 982 assertFalse(message1.isSet(Flag.SEEN)); 983 984 FetchProfile fp = new FetchProfile(); 985 fp.clear(); 986 fp.add(FetchProfile.Item.FLAGS); 987 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID FLAGS\\)", 988 new String[] { 989 "* 1 fETCH (uID 1 fLAGS (\\Seen))", 990 "* 2 fETCH (fLAGS (\\Seen))", 991 getNextTag(true) + " oK SUCCESS" 992 }); 993 994 // Shouldn't crash 995 mFolder.fetch(new Message[] { message1 }, fp, null); 996 997 // And the message is "SEEN". 998 assertTrue(message1.isSet(Flag.SEEN)); 999 } 1000 1001 1002 private ImapMessage prepareForAppendTest(MockTransport mock, String response) throws Exception { 1003 ImapMessage message = (ImapMessage) mFolder.createMessage("initial uid"); 1004 message.setFrom(new Address("me (at) test.com")); 1005 message.setRecipient(RecipientType.TO, new Address("you (at) test.com")); 1006 message.setMessageId("<message.id (at) test.com>"); 1007 message.setFlagDirectlyForTest(Flag.SEEN, true); 1008 message.setBody(new TextBody("Test Body")); 1009 1010 // + go ahead 1011 // * 12345 EXISTS 1012 // OK [APPENDUID 627684530 17] (Success) 1013 1014 mock.expect(getNextTag(false) + 1015 " APPEND \\\"" + FOLDER_ENCODED + "\\\" \\(\\\\SEEN\\) \\{166\\}", 1016 new String[] {"+ gO aHead"}); 1017 1018 mock.expectLiterally("From: me (at) test.com", NO_REPLY); 1019 mock.expectLiterally("To: you (at) test.com", NO_REPLY); 1020 mock.expectLiterally("Message-ID: <message.id (at) test.com>", NO_REPLY); 1021 mock.expectLiterally("Content-Type: text/plain;", NO_REPLY); 1022 mock.expectLiterally(" charset=utf-8", NO_REPLY); 1023 mock.expectLiterally("Content-Transfer-Encoding: base64", NO_REPLY); 1024 mock.expectLiterally("", NO_REPLY); 1025 mock.expectLiterally("VGVzdCBCb2R5", NO_REPLY); 1026 mock.expectLiterally("", new String[] { 1027 "* 7 eXISTS", 1028 getNextTag(true) + " " + response 1029 }); 1030 return message; 1031 } 1032 1033 /** 1034 * Test for APPEND when the response has APPENDUID. 1035 */ 1036 public void testAppendMessages() throws Exception { 1037 MockTransport mock = openAndInjectMockTransport(); 1038 setupOpenFolder(mock); 1039 mFolder.open(OpenMode.READ_WRITE, null); 1040 1041 ImapMessage message = prepareForAppendTest(mock, "oK [aPPENDUID 1234567 13] (Success)"); 1042 1043 mFolder.appendMessages(new Message[] {message}); 1044 1045 assertEquals("13", message.getUid()); 1046 assertEquals(7, mFolder.getMessageCount()); 1047 } 1048 1049 /** 1050 * Test for APPEND when the response doesn't have APPENDUID. 1051 */ 1052 public void testAppendMessagesNoAppendUid() throws Exception { 1053 MockTransport mock = openAndInjectMockTransport(); 1054 setupOpenFolder(mock); 1055 mFolder.open(OpenMode.READ_WRITE, null); 1056 1057 ImapMessage message = prepareForAppendTest(mock, "OK Success"); 1058 1059 mock.expectLiterally( 1060 getNextTag(false) + " UID SEARCH (HEADER MESSAGE-ID <message.id (at) test.com>)", 1061 new String[] { 1062 "* sEARCH 321", 1063 getNextTag(true) + " oK success" 1064 }); 1065 1066 mFolder.appendMessages(new Message[] {message}); 1067 1068 assertEquals("321", message.getUid()); 1069 } 1070 1071 /** 1072 * Test for append failure. 1073 * 1074 * We don't check the response for APPEND. We just SEARCH for the message-id to get the UID. 1075 * If append has failed, the SEARCH command returns no UID, and the UID of the message is left 1076 * unset. 1077 */ 1078 public void testAppendFailure() throws Exception { 1079 MockTransport mock = openAndInjectMockTransport(); 1080 setupOpenFolder(mock); 1081 mFolder.open(OpenMode.READ_WRITE, null); 1082 1083 ImapMessage message = prepareForAppendTest(mock, "NO No space left on the server."); 1084 assertEquals("initial uid", message.getUid()); 1085 1086 mock.expectLiterally( 1087 getNextTag(false) + " UID SEARCH (HEADER MESSAGE-ID <message.id (at) test.com>)", 1088 new String[] { 1089 "* sEARCH", // not found 1090 getNextTag(true) + " oK Search completed." 1091 }); 1092 1093 mFolder.appendMessages(new Message[] {message}); 1094 1095 // Shouldn't have changed 1096 assertEquals("initial uid", message.getUid()); 1097 } 1098 1099 public void testGetPersonalNamespaces() throws Exception { 1100 MockTransport mock = openAndInjectMockTransport(); 1101 expectLogin(mock); 1102 1103 mock.expect(getNextTag(false) + " LIST \"\" \"\\*\"", 1104 new String[] { 1105 "* lIST (\\HAsNoChildren) \"/\" \"inbox\"", 1106 "* lIST (\\hAsnochildren) \"/\" \"Drafts\"", 1107 "* lIST (\\nOselect) \"/\" \"no select\"", 1108 "* lIST (\\HAsNoChildren) \"/\" \"&ZeVnLIqe-\"", // Japanese folder name 1109 getNextTag(true) + " oK SUCCESS" 1110 }); 1111 Folder[] folders = mStore.getPersonalNamespaces(); 1112 1113 ArrayList<String> list = new ArrayList<String>(); 1114 for (Folder f : folders) { 1115 list.add(f.getName()); 1116 } 1117 MoreAsserts.assertEquals( 1118 new String[] {"Drafts", "\u65E5\u672C\u8A9E", "INBOX"}, 1119 list.toArray(new String[0]) 1120 ); 1121 1122 // TODO test with path prefix 1123 // TODO: Test NO response. 1124 } 1125 1126 public void testEncodeFolderName() { 1127 assertEquals("", ImapStore.encodeFolderName("")); 1128 assertEquals("a", ImapStore.encodeFolderName("a")); 1129 assertEquals("XYZ", ImapStore.encodeFolderName("XYZ")); 1130 assertEquals("&ZeVnLIqe-", ImapStore.encodeFolderName("\u65E5\u672C\u8A9E")); 1131 assertEquals("!&ZeVnLIqe-!", ImapStore.encodeFolderName("!\u65E5\u672C\u8A9E!")); 1132 } 1133 1134 public void testDecodeFolderName() { 1135 assertEquals("", ImapStore.decodeFolderName("")); 1136 assertEquals("a", ImapStore.decodeFolderName("a")); 1137 assertEquals("XYZ", ImapStore.decodeFolderName("XYZ")); 1138 assertEquals("\u65E5\u672C\u8A9E", ImapStore.decodeFolderName("&ZeVnLIqe-")); 1139 assertEquals("!\u65E5\u672C\u8A9E!", ImapStore.decodeFolderName("!&ZeVnLIqe-!")); 1140 } 1141 1142 public void testOpen() throws Exception { 1143 MockTransport mock = openAndInjectMockTransport(); 1144 expectLogin(mock); 1145 1146 final Folder folder = mStore.getFolder("test"); 1147 1148 // Not exist 1149 mock.expect(getNextTag(false) + " SELECT \\\"test\\\"", 1150 new String[] { 1151 getNextTag(true) + " nO no such mailbox" 1152 }); 1153 try { 1154 folder.open(OpenMode.READ_WRITE, null); 1155 fail(); 1156 } catch (MessagingException expected) { 1157 } 1158 1159 // READ-WRITE 1160 expectNoop(mock, true); // Need it because we reuse the connection. 1161 mock.expect(getNextTag(false) + " SELECT \\\"test\\\"", 1162 new String[] { 1163 "* 1 eXISTS", 1164 getNextTag(true) + " oK [rEAD-wRITE]" 1165 }); 1166 1167 folder.open(OpenMode.READ_WRITE, null); 1168 assertTrue(folder.exists()); 1169 assertEquals(1, folder.getMessageCount()); 1170 assertEquals(OpenMode.READ_WRITE, folder.getMode()); 1171 1172 assertTrue(folder.isOpen()); 1173 folder.close(false); 1174 assertFalse(folder.isOpen()); 1175 1176 // READ-ONLY 1177 expectNoop(mock, true); // Need it because we reuse the connection. 1178 mock.expect(getNextTag(false) + " SELECT \\\"test\\\"", 1179 new String[] { 1180 "* 2 eXISTS", 1181 getNextTag(true) + " oK [rEAD-oNLY]" 1182 }); 1183 1184 folder.open(OpenMode.READ_WRITE, null); 1185 assertTrue(folder.exists()); 1186 assertEquals(2, folder.getMessageCount()); 1187 assertEquals(OpenMode.READ_ONLY, folder.getMode()); 1188 1189 // Try to re-open as read-write. Should send SELECT again. 1190 expectNoop(mock, true); // Need it because we reuse the connection. 1191 mock.expect(getNextTag(false) + " SELECT \\\"test\\\"", 1192 new String[] { 1193 "* 15 eXISTS", 1194 getNextTag(true) + " oK selected" 1195 }); 1196 1197 folder.open(OpenMode.READ_WRITE, null); 1198 assertTrue(folder.exists()); 1199 assertEquals(15, folder.getMessageCount()); 1200 assertEquals(OpenMode.READ_WRITE, folder.getMode()); 1201 } 1202 1203 public void testExists() throws Exception { 1204 MockTransport mock = openAndInjectMockTransport(); 1205 expectLogin(mock); 1206 1207 // Folder exists 1208 Folder folder = mStore.getFolder("\u65E5\u672C\u8A9E"); 1209 mock.expect(getNextTag(false) + " STATUS \\\"&ZeVnLIqe-\\\" \\(UIDVALIDITY\\)", 1210 new String[] { 1211 "* sTATUS \"&ZeVnLIqe-\" (mESSAGES 10)", 1212 getNextTag(true) + " oK SUCCESS" 1213 }); 1214 1215 assertTrue(folder.exists()); 1216 1217 // Connection verification 1218 expectNoop(mock, true); 1219 1220 // Doesn't exist 1221 folder = mStore.getFolder("no such folder"); 1222 mock.expect(getNextTag(false) + " STATUS \\\"no such folder\\\" \\(UIDVALIDITY\\)", 1223 new String[] { 1224 getNextTag(true) + " NO No such folder!" 1225 }); 1226 1227 assertFalse(folder.exists()); 1228 } 1229 1230 public void testCreate() throws Exception { 1231 MockTransport mock = openAndInjectMockTransport(); 1232 expectLogin(mock); 1233 1234 // Success 1235 Folder folder = mStore.getFolder("\u65E5\u672C\u8A9E"); 1236 1237 assertTrue(folder.canCreate(FolderType.HOLDS_MESSAGES)); 1238 1239 mock.expect(getNextTag(false) + " CREATE \\\"&ZeVnLIqe-\\\"", 1240 new String[] { 1241 getNextTag(true) + " oK Success" 1242 }); 1243 1244 assertTrue(folder.create(FolderType.HOLDS_MESSAGES)); 1245 1246 // Connection verification 1247 expectNoop(mock, true); 1248 1249 // Failure 1250 mock.expect(getNextTag(false) + " CREATE \\\"&ZeVnLIqe-\\\"", 1251 new String[] { 1252 getNextTag(true) + " nO Can't create folder" 1253 }); 1254 1255 assertFalse(folder.create(FolderType.HOLDS_MESSAGES)); 1256 } 1257 1258 public void testCopy() throws Exception { 1259 MockTransport mock = openAndInjectMockTransport(); 1260 setupOpenFolder(mock); 1261 mFolder.open(OpenMode.READ_WRITE, null); 1262 1263 Folder folderTo = mStore.getFolder("\u65E5\u672C\u8A9E"); 1264 Message[] messages = new Message[] { 1265 mFolder.createMessage("11"), 1266 mFolder.createMessage("12"), 1267 }; 1268 1269 mock.expect(getNextTag(false) + " UID COPY 11\\,12 \\\"&ZeVnLIqe-\\\"", 1270 new String[] { 1271 getNextTag(true) + " oK copy completed" 1272 }); 1273 1274 mFolder.copyMessages(messages, folderTo, null); 1275 1276 // TODO: Test NO response. (src message not found) 1277 } 1278 1279 public void testGetUnreadMessageCount() throws Exception { 1280 MockTransport mock = openAndInjectMockTransport(); 1281 setupOpenFolder(mock); 1282 mFolder.open(OpenMode.READ_WRITE, null); 1283 1284 mock.expect(getNextTag(false) + " STATUS \\\"" + FOLDER_ENCODED + "\\\" \\(UNSEEN\\)", 1285 new String[] { 1286 "* sTATUS \"" + FOLDER_ENCODED + "\" (X 1 uNSEEN 123)", 1287 getNextTag(true) + " oK copy completed" 1288 }); 1289 1290 assertEquals(123, mFolder.getUnreadMessageCount()); 1291 } 1292 1293 public void testExpunge() throws Exception { 1294 MockTransport mock = openAndInjectMockTransport(); 1295 setupOpenFolder(mock); 1296 mFolder.open(OpenMode.READ_WRITE, null); 1297 1298 mock.expect(getNextTag(false) + " EXPUNGE", 1299 new String[] { 1300 getNextTag(true) + " oK success" 1301 }); 1302 1303 mFolder.expunge(); 1304 1305 // TODO: Test NO response. (permission denied) 1306 } 1307 1308 public void testSetFlags() throws Exception { 1309 MockTransport mock = openAndInjectMockTransport(); 1310 setupOpenFolder(mock); 1311 mFolder.open(OpenMode.READ_WRITE, null); 1312 1313 Message[] messages = new Message[] { 1314 mFolder.createMessage("11"), 1315 mFolder.createMessage("12"), 1316 }; 1317 1318 // Set 1319 mock.expect( 1320 getNextTag(false) + " UID STORE 11\\,12 \\+FLAGS.SILENT \\(\\\\FLAGGED \\\\SEEN\\)", 1321 new String[] { 1322 getNextTag(true) + " oK success" 1323 }); 1324 mFolder.setFlags(messages, new Flag[] {Flag.FLAGGED, Flag.SEEN}, true); 1325 1326 // Clear 1327 mock.expect( 1328 getNextTag(false) + " UID STORE 11\\,12 \\-FLAGS.SILENT \\(\\\\DELETED\\)", 1329 new String[] { 1330 getNextTag(true) + " oK success" 1331 }); 1332 mFolder.setFlags(messages, new Flag[] {Flag.DELETED}, false); 1333 1334 // TODO: Test NO response. (src message not found) 1335 } 1336 1337 public void testSearchForUids() throws Exception { 1338 MockTransport mock = openAndInjectMockTransport(); 1339 setupOpenFolder(mock); 1340 mFolder.open(OpenMode.READ_WRITE, null); 1341 1342 // Single results 1343 mock.expect( 1344 getNextTag(false) + " UID SEARCH X", 1345 new String[] { 1346 "* sEARCH 1", 1347 getNextTag(true) + " oK success" 1348 }); 1349 MoreAsserts.assertEquals(new String[] { 1350 "1" 1351 }, mFolder.searchForUids("X")); 1352 1353 // Multiple results, including SEARCH with no UIDs. 1354 mock.expect( 1355 getNextTag(false) + " UID SEARCH UID 123", 1356 new String[] { 1357 "* sEARCH 123 4 567", 1358 "* search", 1359 "* sEARCH 0", 1360 "* SEARCH", 1361 "* sEARCH 100 200 300", 1362 getNextTag(true) + " oK success" 1363 }); 1364 MoreAsserts.assertEquals(new String[] { 1365 "123", "4", "567", "0", "100", "200", "300" 1366 }, mFolder.searchForUids("UID 123")); 1367 1368 // NO result 1369 mock.expect( 1370 getNextTag(false) + " UID SEARCH SOME CRITERIA", 1371 new String[] { 1372 getNextTag(true) + " nO not found" 1373 }); 1374 MoreAsserts.assertEquals(new String[] { 1375 }, mFolder.searchForUids("SOME CRITERIA")); 1376 1377 // OK result, but result is empty. (Probably against RFC) 1378 mock.expect( 1379 getNextTag(false) + " UID SEARCH SOME CRITERIA", 1380 new String[] { 1381 getNextTag(true) + " oK success" 1382 }); 1383 MoreAsserts.assertEquals(new String[] { 1384 }, mFolder.searchForUids("SOME CRITERIA")); 1385 1386 // OK result with empty search response. 1387 mock.expect( 1388 getNextTag(false) + " UID SEARCH SOME CRITERIA", 1389 new String[] { 1390 "* search", 1391 getNextTag(true) + " oK success" 1392 }); 1393 MoreAsserts.assertEquals(new String[] { 1394 }, mFolder.searchForUids("SOME CRITERIA")); 1395 } 1396 1397 1398 public void testGetMessage() throws Exception { 1399 MockTransport mock = openAndInjectMockTransport(); 1400 setupOpenFolder(mock); 1401 mFolder.open(OpenMode.READ_WRITE, null); 1402 1403 // Found 1404 mock.expect( 1405 getNextTag(false) + " UID SEARCH UID 123", 1406 new String[] { 1407 "* sEARCH 123", 1408 getNextTag(true) + " oK success" 1409 }); 1410 assertEquals("123", mFolder.getMessage("123").getUid()); 1411 1412 // Not found 1413 mock.expect( 1414 getNextTag(false) + " UID SEARCH UID 123", 1415 new String[] { 1416 getNextTag(true) + " nO not found" 1417 }); 1418 assertNull(mFolder.getMessage("123")); 1419 } 1420 1421 /** Test for getMessages(int, int, MessageRetrievalListener) */ 1422 public void testGetMessages1() throws Exception { 1423 MockTransport mock = openAndInjectMockTransport(); 1424 setupOpenFolder(mock); 1425 mFolder.open(OpenMode.READ_WRITE, null); 1426 1427 // Found 1428 mock.expect( 1429 getNextTag(false) + " UID SEARCH 3:5 NOT DELETED", 1430 new String[] { 1431 "* sEARCH 3 4", 1432 getNextTag(true) + " oK success" 1433 }); 1434 1435 checkMessageUids(new String[] {"3", "4"}, mFolder.getMessages(3, 5, null)); 1436 1437 // Not found 1438 mock.expect( 1439 getNextTag(false) + " UID SEARCH 3:5 NOT DELETED", 1440 new String[] { 1441 getNextTag(true) + " nO not found" 1442 }); 1443 1444 checkMessageUids(new String[] {}, mFolder.getMessages(3, 5, null)); 1445 } 1446 1447 /** 1448 * Test for getMessages(String[] uids, MessageRetrievalListener) where uids != null. 1449 * (testGetMessages3() covers the case where uids == null.) 1450 */ 1451 public void testGetMessages2() throws Exception { 1452 MockTransport mock = openAndInjectMockTransport(); 1453 setupOpenFolder(mock); 1454 mFolder.open(OpenMode.READ_WRITE, null); 1455 1456 // No command will be sent 1457 checkMessageUids(new String[] {"3", "4", "5"}, 1458 mFolder.getMessages(new String[] {"3", "4", "5"}, null)); 1459 1460 checkMessageUids(new String[] {}, 1461 mFolder.getMessages(new String[] {}, null)); 1462 } 1463 1464 /** 1465 * Test for getMessages(MessageRetrievalListener), which is the same as 1466 * getMessages(String[] uids, MessageRetrievalListener) where uids == null. 1467 */ 1468 public void testGetMessages3() throws Exception { 1469 MockTransport mock = openAndInjectMockTransport(); 1470 setupOpenFolder(mock); 1471 mFolder.open(OpenMode.READ_WRITE, null); 1472 1473 mock.expect( 1474 getNextTag(false) + " UID SEARCH 1:\\* NOT DELETED", 1475 new String[] { 1476 "* sEARCH 3 4 5", 1477 getNextTag(true) + " OK success" 1478 }); 1479 checkMessageUids(new String[] {"3", "4", "5"}, 1480 mFolder.getMessages(null)); 1481 } 1482 1483 private static void checkMessageUids(String[] expectedUids, Message[] actualMessages) { 1484 ArrayList<String> list = new ArrayList<String>(); 1485 for (Message m : actualMessages) { 1486 list.add(m.getUid()); 1487 } 1488 MoreAsserts.assertEquals(expectedUids, list.toArray(new String[0]) ); 1489 } 1490 1491 /** 1492 * Test for {@link ImapStore#getConnection} 1493 */ 1494 public void testGetConnection() throws Exception { 1495 MockTransport mock = openAndInjectMockTransport(); 1496 1497 // Start: No pooled connections. 1498 assertEquals(0, mStore.getConnectionPoolForTest().size()); 1499 1500 // Get 1st connection. 1501 final ImapConnection con1 = mStore.getConnection(); 1502 assertNotNull(con1); 1503 assertEquals(0, mStore.getConnectionPoolForTest().size()); // Pool size not changed. 1504 assertFalse(con1.isTransportOpenForTest()); // Transport not open yet. 1505 1506 // Open con1 1507 expectLogin(mock); 1508 con1.open(); 1509 assertTrue(con1.isTransportOpenForTest()); 1510 1511 // Get 2nd connection. 1512 final ImapConnection con2 = mStore.getConnection(); 1513 assertNotNull(con2); 1514 assertEquals(0, mStore.getConnectionPoolForTest().size()); // Pool size not changed. 1515 assertFalse(con2.isTransportOpenForTest()); // Transport not open yet. 1516 1517 // con1 != con2 1518 assertNotSame(con1, con2); 1519 1520 // Open con2 1521 expectLogin(mock); 1522 con2.open(); 1523 assertTrue(con1.isTransportOpenForTest()); 1524 1525 // Now we have two open connections: con1 and con2 1526 1527 // Save con1 in the pool. 1528 mStore.poolConnection(con1); 1529 assertEquals(1, mStore.getConnectionPoolForTest().size()); 1530 1531 // Get another connection. Should get con1, after verifying the connection. 1532 mock.expect(getNextTag(false) + " NOOP", new String[] {getNextTag(true) + " oK success"}); 1533 1534 final ImapConnection con1b = mStore.getConnection(); 1535 assertEquals(0, mStore.getConnectionPoolForTest().size()); // No connections left in pool 1536 assertSame(con1, con1b); 1537 assertTrue(con1.isTransportOpenForTest()); // We opened it. 1538 1539 // Save con2. 1540 mStore.poolConnection(con2); 1541 assertEquals(1, mStore.getConnectionPoolForTest().size()); 1542 1543 // Try to get connection, but this time, connection gets closed. 1544 mock.expect(getNextTag(false) + " NOOP", new String[] {getNextTag(true) + "* bYE bye"}); 1545 final ImapConnection con3 = mStore.getConnection(); 1546 assertNotNull(con3); 1547 assertEquals(0, mStore.getConnectionPoolForTest().size()); // No connections left in pool 1548 1549 // It should be a new connection. 1550 assertNotSame(con1, con3); 1551 assertNotSame(con2, con3); 1552 } 1553 1554 public void testCheckSettings() throws Exception { 1555 MockTransport mock = openAndInjectMockTransport(); 1556 1557 expectLogin(mock); 1558 mStore.checkSettings(); 1559 1560 expectLogin(mock, false, false, 1561 new String[] {"* iD nIL", "oK"}, "nO authentication failed"); 1562 try { 1563 mStore.checkSettings(); 1564 fail(); 1565 } catch (MessagingException expected) { 1566 } 1567 } 1568 1569 // Compatibility tests... 1570 1571 /** 1572 * Getting an ALERT with a % mark in the message, which crashed the old parser. 1573 */ 1574 public void testQuotaAlert() throws Exception { 1575 MockTransport mock = openAndInjectMockTransport(); 1576 expectLogin(mock); 1577 1578 // Success 1579 Folder folder = mStore.getFolder("INBOX"); 1580 1581 // The following response was copied from an actual bug... 1582 mock.expect(getNextTag(false) + " SELECT \"INBOX\"", new String[] { 1583 "* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen NonJunk $Forwarded Junk" + 1584 " $Label4 $Label1 $Label2 $Label3 $Label5 $MDNSent Old)", 1585 "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen NonJunk" + 1586 " $Forwarded Junk $Label4 $Label1 $Label2 $Label3 $Label5 $MDNSent Old \\*)]", 1587 "* 6406 EXISTS", 1588 "* 0 RECENT", 1589 "* OK [UNSEEN 5338]", 1590 "* OK [UIDVALIDITY 1055957975]", 1591 "* OK [UIDNEXT 449625]", 1592 "* NO [ALERT] Mailbox is at 98% of quota", 1593 getNextTag(true) + " OK [READ-WRITE] Completed"}); 1594 folder.open(OpenMode.READ_WRITE, null); // shouldn't crash. 1595 assertEquals(6406, folder.getMessageCount()); 1596 } 1597 1598 /** 1599 * Apparently some servers send a size in the wrong format. e.g. 123E 1600 */ 1601 public void testFetchBodyStructureMalformed() throws Exception { 1602 final MockTransport mock = openAndInjectMockTransport(); 1603 setupOpenFolder(mock); 1604 mFolder.open(OpenMode.READ_WRITE, null); 1605 final Message message = mFolder.createMessage("1"); 1606 1607 final FetchProfile fp = new FetchProfile(); 1608 fp.add(FetchProfile.Item.STRUCTURE); 1609 mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)", 1610 new String[] { 1611 "* 9 FETCH (UID 1 BODYSTRUCTURE (\"TEXT\" \"PLAIN\" ()" + 1612 " NIL NIL NIL 123E 3))", // 123E isn't a number! 1613 getNextTag(true) + " OK SUCCESS" 1614 }); 1615 mFolder.fetch(new Message[] { message }, fp, null); 1616 1617 // Check mime structure... 1618 MoreAsserts.assertEquals( 1619 new String[] {"text/plain"}, 1620 message.getHeader("Content-Type") 1621 ); 1622 assertNull(message.getHeader("Content-Transfer-Encoding")); 1623 assertNull(message.getHeader("Content-ID")); 1624 1625 // Doesn't have size=xxx 1626 assertNull(message.getHeader("Content-Disposition")); 1627 } 1628 1629 /** 1630 * Folder name with special chars in it. 1631 * 1632 * Gmail puts the folder name in the OK response, which crashed the old parser if there's a 1633 * special char in the folder name. 1634 */ 1635 public void testFolderNameWithSpecialChars() throws Exception { 1636 final String FOLDER_1 = "@u88**%_St"; 1637 final String FOLDER_1_QUOTED = Pattern.quote(FOLDER_1); 1638 final String FOLDER_2 = "folder test_06"; 1639 1640 MockTransport mock = openAndInjectMockTransport(); 1641 expectLogin(mock); 1642 1643 // List folders. 1644 mock.expect(getNextTag(false) + " LIST \"\" \"\\*\"", 1645 new String[] { 1646 "* LIST () \"/\" \"" + FOLDER_1 + "\"", 1647 "* LIST () \"/\" \"" + FOLDER_2 + "\"", 1648 getNextTag(true) + " OK SUCCESS" 1649 }); 1650 final Folder[] folders = mStore.getPersonalNamespaces(); 1651 1652 ArrayList<String> list = new ArrayList<String>(); 1653 for (Folder f : folders) { 1654 list.add(f.getName()); 1655 } 1656 MoreAsserts.assertEquals( 1657 new String[] {FOLDER_1, FOLDER_2, "INBOX"}, 1658 list.toArray(new String[0]) 1659 ); 1660 1661 // Try to open the folders. 1662 expectNoop(mock, true); 1663 mock.expect(getNextTag(false) + " SELECT \"" + FOLDER_1_QUOTED + "\"", new String[] { 1664 "* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)", 1665 "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen \\*)]", 1666 "* 0 EXISTS", 1667 "* 0 RECENT", 1668 "* OK [UNSEEN 0]", 1669 "* OK [UIDNEXT 1]", 1670 getNextTag(true) + " OK [READ-WRITE] " + FOLDER_1}); 1671 folders[0].open(OpenMode.READ_WRITE, null); 1672 folders[0].close(false); 1673 1674 expectNoop(mock, true); 1675 mock.expect(getNextTag(false) + " SELECT \"" + FOLDER_2 + "\"", new String[] { 1676 "* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)", 1677 "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen \\*)]", 1678 "* 0 EXISTS", 1679 "* 0 RECENT", 1680 "* OK [UNSEEN 0]", 1681 "* OK [UIDNEXT 1]", 1682 getNextTag(true) + " OK [READ-WRITE] " + FOLDER_2}); 1683 folders[1].open(OpenMode.READ_WRITE, null); 1684 folders[1].close(false); 1685 } 1686 1687 /** 1688 * Callback for {@link #runAndExpectMessagingException}. 1689 */ 1690 private interface RunAndExpectMessagingExceptionTarget { 1691 public void run(MockTransport mockTransport) throws Exception; 1692 } 1693 1694 /** 1695 * Set up the usual mock transport, open the folder, 1696 * run {@link RunAndExpectMessagingExceptionTarget} and make sure a {@link MessagingException} 1697 * is thrown. 1698 */ 1699 private void runAndExpectMessagingException(RunAndExpectMessagingExceptionTarget target) 1700 throws Exception { 1701 try { 1702 final MockTransport mockTransport = openAndInjectMockTransport(); 1703 setupOpenFolder(mockTransport); 1704 mFolder.open(OpenMode.READ_WRITE, null); 1705 1706 target.run(mockTransport); 1707 1708 fail("MessagingException expected."); 1709 } catch (MessagingException expected) { 1710 } 1711 } 1712 1713 /** 1714 * Make sure that IOExceptions are always converted to MessagingException. 1715 */ 1716 public void testFetchIOException() throws Exception { 1717 runAndExpectMessagingException(new RunAndExpectMessagingExceptionTarget() { 1718 public void run(MockTransport mockTransport) throws Exception { 1719 mockTransport.expectIOException(); 1720 1721 final Message message = mFolder.createMessage("1"); 1722 final FetchProfile fp = new FetchProfile(); 1723 fp.add(FetchProfile.Item.STRUCTURE); 1724 1725 mFolder.fetch(new Message[] { message }, fp, null); 1726 } 1727 }); 1728 } 1729 1730 /** 1731 * Make sure that IOExceptions are always converted to MessagingException. 1732 */ 1733 public void testUnreadMessageCountIOException() throws Exception { 1734 runAndExpectMessagingException(new RunAndExpectMessagingExceptionTarget() { 1735 public void run(MockTransport mockTransport) throws Exception { 1736 mockTransport.expectIOException(); 1737 1738 mFolder.getUnreadMessageCount(); 1739 } 1740 }); 1741 } 1742 1743 /** 1744 * Make sure that IOExceptions are always converted to MessagingException. 1745 */ 1746 public void testCopyMessagesIOException() throws Exception { 1747 runAndExpectMessagingException(new RunAndExpectMessagingExceptionTarget() { 1748 public void run(MockTransport mockTransport) throws Exception { 1749 mockTransport.expectIOException(); 1750 1751 final Message message = mFolder.createMessage("1"); 1752 final Folder folder = mStore.getFolder("test"); 1753 1754 mFolder.copyMessages(new Message[] { message }, folder, null); 1755 } 1756 }); 1757 } 1758 1759 /** 1760 * Make sure that IOExceptions are always converted to MessagingException. 1761 */ 1762 public void testSearchForUidsIOException() throws Exception { 1763 runAndExpectMessagingException(new RunAndExpectMessagingExceptionTarget() { 1764 public void run(MockTransport mockTransport) throws Exception { 1765 mockTransport.expectIOException(); 1766 1767 mFolder.getMessage("uid"); 1768 } 1769 }); 1770 } 1771 1772 /** 1773 * Make sure that IOExceptions are always converted to MessagingException. 1774 */ 1775 public void testExpungeIOException() throws Exception { 1776 runAndExpectMessagingException(new RunAndExpectMessagingExceptionTarget() { 1777 public void run(MockTransport mockTransport) throws Exception { 1778 mockTransport.expectIOException(); 1779 1780 mFolder.expunge(); 1781 } 1782 }); 1783 } 1784 1785 /** 1786 * Make sure that IOExceptions are always converted to MessagingException. 1787 */ 1788 public void testOpenIOException() throws Exception { 1789 runAndExpectMessagingException(new RunAndExpectMessagingExceptionTarget() { 1790 public void run(MockTransport mockTransport) throws Exception { 1791 mockTransport.expectIOException(); 1792 final Folder folder = mStore.getFolder("test"); 1793 folder.open(OpenMode.READ_WRITE, null); 1794 } 1795 }); 1796 } 1797 } 1798