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