• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getContext()55     private Context getContext() {
56         return getInstrumentation().getTargetContext();
57     }
58 
59     @Override
setUp()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
tearDown()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      */
checkControllerResultRemoved(Controller controller)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      */
createAccount(boolean securityHold)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      */
createMailbox(long accountId, int mailboxType)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      */
createAndStartFinder(final long accountId, final int mailboxType)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      */
waitUntilCallbackCalled()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      */
testSecurityHold()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      */
testAccountNotFound()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      */
testMailboxFound()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      */
prepareForNetworkLookupTest(final long accountId)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      */
testMailboxNotFound()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      */
testMailboxFoundOnNetwork()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      */
testMailboxNotFoundNetworkError()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      */
testUpdateMailboxListCallbackNonTarget()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      */
testMailboxNotFound2()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      */
testRunTwice()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 
testCancel()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 
reset()397         public void reset() {
398             mPassedAccountId = -1;
399             mCalledUpdateMailboxList = false;
400         }
401 
MockController(Context context)402         protected MockController(Context context) {
403             super(context);
404         }
405 
406         @Override
updateMailboxList(long accountId)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 
isAnyMethodCalled()425         public boolean isAnyMethodCalled() {
426             return mCalledAccountNotFound || mCalledAccountSecurityHold || mCalledMailboxFound
427                     || mCalledMailboxNotFound;
428         }
429 
430         @Override
onAccountNotFound()431         public void onAccountNotFound() {
432             mCalledAccountNotFound = true;
433         }
434 
435         @Override
onAccountSecurityHold(long accountId)436         public void onAccountSecurityHold(long accountId) {
437             mCalledAccountSecurityHold = true;
438             mAccountId = accountId;
439         }
440 
441         @Override
onMailboxFound(long accountId, long mailboxId)442         public void onMailboxFound(long accountId, long mailboxId) {
443             mCalledMailboxFound = true;
444             mAccountId = accountId;
445             mMailboxId = mailboxId;
446         }
447 
448         @Override
onMailboxNotFound(long accountId)449         public void onMailboxNotFound(long accountId) {
450             mCalledMailboxNotFound = true;
451             mAccountId = accountId;
452         }
453     }
454 }
455