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