1 /* 2 * Copyright (C) 2010 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.activity; 18 19 import android.content.Context; 20 import android.test.InstrumentationTestCase; 21 import android.test.ProviderTestCase2; 22 import android.test.suitebuilder.annotation.LargeTest; 23 24 import com.android.email.Controller; 25 import com.android.email.DBTestHelper; 26 import com.android.email.Email; 27 import com.android.email.TestUtils; 28 import com.android.email.provider.ProviderTestUtils; 29 import com.android.emailcommon.mail.MessagingException; 30 import com.android.emailcommon.provider.Account; 31 import com.android.emailcommon.provider.Mailbox; 32 33 /** 34 * Test case for {@link MailboxFinder}. 35 * 36 * We need to use {@link InstrumentationTestCase} so that we can create AsyncTasks on the UI thread 37 * using {@link InstrumentationTestCase#runTestOnUiThread}. This class also needs an isolated 38 * context, which is provided by {@link ProviderTestCase2}. We can't derive from two classes, 39 * so we just copy the code for an isolate context to here. 40 */ 41 @LargeTest 42 public class MailboxFinderTest extends InstrumentationTestCase { 43 private static final int TIMEOUT = 10; // in seconds 44 45 // Test target 46 private MailboxFinder mMailboxFinder; 47 48 // Isolted Context for providers. 49 private Context mProviderContext; 50 51 // Mock to track callback invocations. 52 private MockController mMockController; 53 private MockCallback mCallback; 54 55 private Context getContext() { 56 return getInstrumentation().getTargetContext(); 57 } 58 59 @Override 60 protected void setUp() throws Exception { 61 super.setUp(); 62 63 mProviderContext = DBTestHelper.ProviderContextSetupHelper.getProviderContext( 64 getInstrumentation().getTargetContext()); 65 mCallback = new MockCallback(); 66 mMockController = new MockController(getContext()); 67 Controller.injectMockControllerForTest(mMockController); 68 assertEquals(0, mMockController.getResultCallbacksForTest().size()); 69 } 70 71 @Override 72 protected void tearDown() throws Exception { 73 super.tearDown(); 74 if (mMailboxFinder != null) { 75 mMailboxFinder.cancel(); 76 77 // MailboxFinder should unregister its listener when closed. 78 checkControllerResultRemoved(mMockController); 79 } 80 mMockController.cleanupForTest(); 81 Controller.injectMockControllerForTest(null); 82 } 83 84 /** 85 * Make sure no {@link MailboxFinder.Callback} is left registered to the controller. 86 */ 87 private static void checkControllerResultRemoved(Controller controller) { 88 for (Controller.Result callback : controller.getResultCallbacksForTest()) { 89 assertFalse(callback instanceof MailboxFinder.Callback); 90 } 91 } 92 93 /** 94 * Create an account and returns the ID. 95 */ 96 private long createAccount(boolean securityHold) { 97 Account acct = ProviderTestUtils.setupAccount("acct1", false, mProviderContext); 98 if (securityHold) { 99 acct.mFlags |= Account.FLAGS_SECURITY_HOLD; 100 } 101 acct.save(mProviderContext); 102 return acct.mId; 103 } 104 105 /** 106 * Create a mailbox and return the ID. 107 */ 108 private long createMailbox(long accountId, int mailboxType) { 109 Mailbox box = new Mailbox(); 110 box.mServerId = box.mDisplayName = "mailbox"; 111 box.mAccountKey = accountId; 112 box.mType = mailboxType; 113 box.mFlagVisible = true; 114 box.mVisibleLimit = Email.VISIBLE_LIMIT_DEFAULT; 115 box.save(mProviderContext); 116 return box.mId; 117 } 118 119 /** 120 * Create a {@link MailboxFinder} and kick it. 121 */ 122 private void createAndStartFinder(final long accountId, final int mailboxType) 123 throws Throwable { 124 runTestOnUiThread(new Runnable() { 125 @Override 126 public void run() { 127 mMailboxFinder = new MailboxFinder(mProviderContext, accountId, mailboxType, 128 mCallback); 129 mMailboxFinder.startLookup(); 130 assertTrue(mMailboxFinder.isStartedForTest()); 131 } 132 }); 133 } 134 135 /** 136 * Wait until any of the {@link MailboxFinder.Callback} method or 137 * {@link Controller#updateMailboxList} is called. 138 */ 139 private void waitUntilCallbackCalled() { 140 TestUtils.waitUntil("", new TestUtils.Condition() { 141 @Override 142 public boolean isMet() { 143 return mCallback.isAnyMethodCalled() || mMockController.mCalledUpdateMailboxList; 144 } 145 }, TIMEOUT); 146 } 147 148 /** 149 * Test: Account is on security hold. 150 */ 151 public void testSecurityHold() throws Throwable { 152 final long accountId = createAccount(true); 153 154 createAndStartFinder(accountId, Mailbox.TYPE_INBOX); 155 waitUntilCallbackCalled(); 156 157 assertFalse(mCallback.mCalledAccountNotFound); 158 assertTrue(mCallback.mCalledAccountSecurityHold); 159 assertFalse(mCallback.mCalledMailboxFound); 160 assertFalse(mCallback.mCalledMailboxNotFound); 161 assertFalse(mMockController.mCalledUpdateMailboxList); 162 163 assertTrue(mMailboxFinder.isReallyClosedForTest()); 164 } 165 166 /** 167 * Test: Account does not exist. 168 */ 169 public void testAccountNotFound() throws Throwable { 170 createAndStartFinder(123456, Mailbox.TYPE_INBOX); // No such account. 171 waitUntilCallbackCalled(); 172 173 assertTrue(mCallback.mCalledAccountNotFound); 174 assertFalse(mCallback.mCalledAccountSecurityHold); 175 assertFalse(mCallback.mCalledMailboxFound); 176 assertFalse(mCallback.mCalledMailboxNotFound); 177 assertFalse(mMockController.mCalledUpdateMailboxList); 178 179 assertTrue(mMailboxFinder.isReallyClosedForTest()); 180 } 181 182 /** 183 * Test: Mailbox found 184 */ 185 public void testMailboxFound() throws Throwable { 186 final long accountId = createAccount(false); 187 final long mailboxId = createMailbox(accountId, Mailbox.TYPE_INBOX); 188 189 createAndStartFinder(accountId, Mailbox.TYPE_INBOX); 190 waitUntilCallbackCalled(); 191 192 assertFalse(mCallback.mCalledAccountNotFound); 193 assertFalse(mCallback.mCalledAccountSecurityHold); 194 assertTrue(mCallback.mCalledMailboxFound); 195 assertFalse(mCallback.mCalledMailboxNotFound); 196 assertFalse(mMockController.mCalledUpdateMailboxList); 197 198 assertEquals(accountId, mCallback.mAccountId); 199 assertEquals(mailboxId, mCallback.mMailboxId); 200 201 assertTrue(mMailboxFinder.isReallyClosedForTest()); 202 } 203 204 /** 205 * Common initialization for tests that involves network-lookup. 206 */ 207 private void prepareForNetworkLookupTest(final long accountId) throws Throwable { 208 // Look for non-existing mailbox. 209 createAndStartFinder(accountId, Mailbox.TYPE_INBOX); 210 waitUntilCallbackCalled(); 211 212 // Mailbox not found, so the finder should try network-looking up. 213 assertFalse(mCallback.mCalledAccountNotFound); 214 assertFalse(mCallback.mCalledAccountSecurityHold); 215 assertFalse(mCallback.mCalledMailboxFound); 216 assertFalse(mCallback.mCalledMailboxNotFound); 217 218 // Controller.updateMailboxList() should have been called, with the account id. 219 assertTrue(mMockController.mCalledUpdateMailboxList); 220 assertEquals(accountId, mMockController.mPassedAccountId); 221 222 mMockController.reset(); 223 224 assertFalse(mMailboxFinder.isReallyClosedForTest()); // Not closed yet 225 } 226 227 /** 228 * Test: Account exists, but mailbox doesn't -> Get {@link Controller} to update the mailbox 229 * list -> mailbox still doesn't exist. 230 */ 231 public void testMailboxNotFound() throws Throwable { 232 final long accountId = createAccount(false); 233 234 prepareForNetworkLookupTest(accountId); 235 236 // Imitate the mCallback... 237 runTestOnUiThread(new Runnable() { 238 @Override 239 public void run() { 240 mMailboxFinder.getControllerResultsForTest().updateMailboxListCallback( 241 null, accountId, 100); 242 } 243 }); 244 245 // Task should have started, so wait for the response... 246 waitUntilCallbackCalled(); 247 248 assertFalse(mCallback.mCalledAccountNotFound); 249 assertFalse(mCallback.mCalledAccountSecurityHold); 250 assertFalse(mCallback.mCalledMailboxFound); 251 assertTrue(mCallback.mCalledMailboxNotFound); 252 assertFalse(mMockController.mCalledUpdateMailboxList); 253 254 assertTrue(mMailboxFinder.isReallyClosedForTest()); 255 } 256 257 /** 258 * Test: Account exists, but mailbox doesn't -> Get {@link Controller} to update the mailbox 259 * list -> found mailbox this time. 260 */ 261 public void testMailboxFoundOnNetwork() throws Throwable { 262 final long accountId = createAccount(false); 263 264 prepareForNetworkLookupTest(accountId); 265 266 // Create mailbox at this point. 267 final long mailboxId = createMailbox(accountId, Mailbox.TYPE_INBOX); 268 269 // Imitate the mCallback... 270 runTestOnUiThread(new Runnable() { 271 @Override 272 public void run() { 273 mMailboxFinder.getControllerResultsForTest().updateMailboxListCallback( 274 null, accountId, 100); 275 } 276 }); 277 278 // Task should have started, so wait for the response... 279 waitUntilCallbackCalled(); 280 281 assertFalse(mCallback.mCalledAccountNotFound); 282 assertFalse(mCallback.mCalledAccountSecurityHold); 283 assertTrue(mCallback.mCalledMailboxFound); 284 assertFalse(mCallback.mCalledMailboxNotFound); 285 assertFalse(mMockController.mCalledUpdateMailboxList); 286 287 assertEquals(accountId, mCallback.mAccountId); 288 assertEquals(mailboxId, mCallback.mMailboxId); 289 290 assertTrue(mMailboxFinder.isReallyClosedForTest()); 291 } 292 293 /** 294 * Test: Account exists, but mailbox doesn't -> Get {@link Controller} to update the mailbox 295 * list -> network error. 296 */ 297 public void testMailboxNotFoundNetworkError() throws Throwable { 298 final long accountId = createAccount(false); 299 300 prepareForNetworkLookupTest(accountId); 301 302 // Imitate the mCallback... 303 runTestOnUiThread(new Runnable() { 304 @Override 305 public void run() { 306 // network error. 307 mMailboxFinder.getControllerResultsForTest().updateMailboxListCallback( 308 new MessagingException("Network error"), accountId, 0); 309 } 310 }); 311 312 assertFalse(mCallback.mCalledAccountNotFound); 313 assertFalse(mCallback.mCalledAccountSecurityHold); 314 assertFalse(mCallback.mCalledMailboxFound); 315 assertTrue(mCallback.mCalledMailboxNotFound); 316 assertFalse(mMockController.mCalledUpdateMailboxList); 317 318 assertTrue(mMailboxFinder.isReallyClosedForTest()); 319 } 320 321 /** 322 * Test: updateMailboxListCallback won't respond to update of a non-target account. 323 */ 324 public void testUpdateMailboxListCallbackNonTarget() throws Throwable { 325 final long accountId = createAccount(false); 326 327 prepareForNetworkLookupTest(accountId); 328 329 // Callback from Controller, but for a different account. 330 runTestOnUiThread(new Runnable() { 331 @Override 332 public void run() { 333 long nonTargetAccountId = accountId + 1; 334 mMailboxFinder.getControllerResultsForTest().updateMailboxListCallback( 335 new MessagingException("Network error"), nonTargetAccountId, 0); 336 } 337 }); 338 339 // Nothing happened. 340 assertFalse(mCallback.mCalledAccountNotFound); 341 assertFalse(mCallback.mCalledAccountSecurityHold); 342 assertFalse(mCallback.mCalledMailboxFound); 343 assertFalse(mCallback.mCalledMailboxNotFound); 344 assertFalse(mMockController.mCalledUpdateMailboxList); 345 346 assertFalse(mMailboxFinder.isReallyClosedForTest()); // Not closed yet 347 } 348 349 /** 350 * Test: Mailbox not found (mailbox of different type exists) 351 */ 352 public void testMailboxNotFound2() throws Throwable { 353 final long accountId = createAccount(false); 354 final long mailboxId = createMailbox(accountId, Mailbox.TYPE_DRAFTS); 355 356 createAndStartFinder(accountId, Mailbox.TYPE_INBOX); 357 waitUntilCallbackCalled(); 358 359 assertFalse(mCallback.mCalledAccountNotFound); 360 assertFalse(mCallback.mCalledAccountSecurityHold); 361 assertFalse(mCallback.mCalledMailboxFound); 362 assertFalse(mCallback.mCalledMailboxNotFound); 363 assertTrue(mMockController.mCalledUpdateMailboxList); 364 365 assertFalse(mMailboxFinder.isReallyClosedForTest()); // Not closed yet -- network lookup. 366 } 367 368 /** 369 * Test: Call {@link MailboxFinder#startLookup()} twice, which should throw an ISE. 370 */ 371 public void testRunTwice() throws Throwable { 372 final long accountId = createAccount(true); 373 374 createAndStartFinder(accountId, Mailbox.TYPE_INBOX); 375 try { 376 mMailboxFinder.startLookup(); 377 fail("Expected exception not thrown"); 378 } catch (IllegalStateException ok) { 379 } 380 } 381 382 public void testCancel() throws Throwable { 383 final long accountId = createAccount(true); 384 385 createAndStartFinder(accountId, Mailbox.TYPE_INBOX); 386 mMailboxFinder.cancel(); 387 assertTrue(mMailboxFinder.isReallyClosedForTest()); 388 } 389 390 /** 391 * A {@link Controller} that remembers if updateMailboxList has been called. 392 */ 393 private static class MockController extends Controller { 394 public volatile long mPassedAccountId; 395 public volatile boolean mCalledUpdateMailboxList; 396 397 public void reset() { 398 mPassedAccountId = -1; 399 mCalledUpdateMailboxList = false; 400 } 401 402 protected MockController(Context context) { 403 super(context); 404 } 405 406 @Override 407 public void updateMailboxList(long accountId) { 408 mCalledUpdateMailboxList = true; 409 mPassedAccountId = accountId; 410 } 411 } 412 413 /** 414 * Callback that logs what method is called with what arguments. 415 */ 416 private static class MockCallback implements MailboxFinder.Callback { 417 public volatile boolean mCalledAccountNotFound; 418 public volatile boolean mCalledAccountSecurityHold; 419 public volatile boolean mCalledMailboxFound; 420 public volatile boolean mCalledMailboxNotFound; 421 422 public volatile long mAccountId = -1; 423 public volatile long mMailboxId = -1; 424 425 public boolean isAnyMethodCalled() { 426 return mCalledAccountNotFound || mCalledAccountSecurityHold || mCalledMailboxFound 427 || mCalledMailboxNotFound; 428 } 429 430 @Override 431 public void onAccountNotFound() { 432 mCalledAccountNotFound = true; 433 } 434 435 @Override 436 public void onAccountSecurityHold(long accountId) { 437 mCalledAccountSecurityHold = true; 438 mAccountId = accountId; 439 } 440 441 @Override 442 public void onMailboxFound(long accountId, long mailboxId) { 443 mCalledMailboxFound = true; 444 mAccountId = accountId; 445 mMailboxId = mailboxId; 446 } 447 448 @Override 449 public void onMailboxNotFound(long accountId) { 450 mCalledMailboxNotFound = true; 451 mAccountId = accountId; 452 } 453 } 454 } 455