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;
18
19import com.android.email.provider.ProviderTestUtils;
20import com.android.emailcommon.Logging;
21import com.android.emailcommon.mail.MessagingException;
22import com.android.emailcommon.provider.Account;
23
24import android.content.Context;
25import android.test.InstrumentationTestCase;
26import android.test.suitebuilder.annotation.LargeTest;
27import android.util.Log;
28
29import junit.framework.Assert;
30
31@LargeTest
32public class RefreshManagerTest extends InstrumentationTestCase {
33    private static final int WAIT_UNTIL_TIMEOUT_SECONDS = 15;
34    private MockClock mClock;
35    private MockController mController;
36    private RefreshManager mTarget;
37    private RefreshListener mListener;
38
39    private Context mContext;
40
41    // Isolated Context for providers.
42    private Context mProviderContext;
43
44    private static final MessagingException EXCEPTION = new MessagingException("test");
45
46    // Looks silly, but it'll make it more readable.
47    private static final long ACCOUNT_1 = 1;
48    private static final long ACCOUNT_2 = 2;
49    private static final long MAILBOX_1 = 3;
50    private static final long MAILBOX_2 = 4;
51
52    @Override
53    protected void setUp() throws Exception {
54        super.setUp();
55
56        mClock = new MockClock();
57        mContext = getInstrumentation().getTargetContext();
58        mController = new MockController(mContext);
59        mListener = new RefreshListener();
60        mProviderContext = DBTestHelper.ProviderContextSetupHelper.getProviderContext(mContext);
61        mTarget = new RefreshManager(mProviderContext, mController, mClock, null);
62        mTarget.registerListener(mListener);
63    }
64
65    @Override
66    protected void tearDown() throws Exception {
67        super.tearDown();
68        mController.cleanupForTest();
69    }
70
71    public void testRegisterUnregisterListener() {
72        // mListener is already registered
73        assertEquals(1, mTarget.getListenersForTest().size());
74
75        mTarget.unregisterListener(mListener);
76        assertEquals(0, mTarget.getListenersForTest().size());
77    }
78
79    public void testRefreshStatus() {
80        RefreshManager.Status s = new RefreshManager.Status();
81        assertFalse(s.isRefreshing());
82        assertTrue(s.canRefresh());
83        assertEquals(0, s.getLastRefreshTime());
84
85        // Request refresh
86        s.onRefreshRequested();
87        assertTrue(s.isRefreshing());
88        assertFalse(s.canRefresh());
89        assertEquals(0, s.getLastRefreshTime());
90
91        // Refresh start
92        s.onCallback(null, 0, mClock);
93        assertTrue(s.isRefreshing());
94        assertFalse(s.canRefresh());
95        assertEquals(0, s.getLastRefreshTime());
96
97        // Refresh 50% done -- nothing changes
98        s.onCallback(null, 50, mClock);
99        assertTrue(s.isRefreshing());
100        assertFalse(s.canRefresh());
101        assertEquals(0, s.getLastRefreshTime());
102
103        // Refresh finish
104        s.onCallback(null, 100, mClock);
105        assertFalse(s.isRefreshing());
106        assertTrue(s.canRefresh());
107        assertEquals(mClock.mTime, s.getLastRefreshTime());
108
109        // Refresh start without request
110        s.onCallback(null, 0, mClock);
111        assertTrue(s.isRefreshing());
112        assertFalse(s.canRefresh());
113        assertEquals(mClock.mTime, s.getLastRefreshTime());
114
115        mClock.advance();
116
117        // Refresh finish with error.
118        s.onCallback(EXCEPTION, 0, mClock);
119        assertFalse(s.isRefreshing());
120        assertTrue(s.canRefresh());
121        assertEquals(mClock.mTime, s.getLastRefreshTime());
122    }
123
124    public void testRefreshMailboxList() {
125        // request refresh for account 1
126        assertTrue(mTarget.refreshMailboxList(ACCOUNT_1));
127
128        assertTrue(mListener.mCalledOnRefreshStatusChanged);
129        assertFalse(mListener.mCalledOnConnectionError);
130        assertEquals(ACCOUNT_1, mListener.mAccountId);
131        assertEquals(-1, mListener.mMailboxId);
132        mListener.reset();
133        assertTrue(mController.mCalledUpdateMailboxList);
134        assertEquals(ACCOUNT_1, mController.mAccountId);
135        assertEquals(-1, mController.mMailboxId);
136        mController.reset();
137        assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_1));
138        assertTrue(mTarget.isRefreshingAnyMailboxListForTest());
139
140        // Request again -- shouldn't be accepted.
141        assertFalse(mTarget.refreshMailboxList(ACCOUNT_1));
142
143        assertFalse(mListener.mCalledOnRefreshStatusChanged);
144        assertFalse(mListener.mCalledOnConnectionError);
145        mListener.reset();
146        assertFalse(mController.mCalledUpdateMailboxList);
147        mController.reset();
148
149        // request refresh for account 2
150        assertTrue(mTarget.refreshMailboxList(ACCOUNT_2));
151
152        assertTrue(mListener.mCalledOnRefreshStatusChanged);
153        assertFalse(mListener.mCalledOnConnectionError);
154        assertEquals(ACCOUNT_2, mListener.mAccountId);
155        assertEquals(-1, mListener.mMailboxId);
156        mListener.reset();
157        assertTrue(mController.mCalledUpdateMailboxList);
158        assertEquals(ACCOUNT_2, mController.mAccountId);
159        assertEquals(-1, mController.mMailboxId);
160        mController.reset();
161        assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_2));
162        assertTrue(mTarget.isRefreshingAnyMailboxListForTest());
163
164        // Refreshing for account 1...
165        mController.mListener.updateMailboxListCallback(null, ACCOUNT_1, 0);
166
167        assertTrue(mListener.mCalledOnRefreshStatusChanged);
168        assertFalse(mListener.mCalledOnConnectionError);
169        assertEquals(ACCOUNT_1, mListener.mAccountId);
170        assertEquals(-1, mListener.mMailboxId);
171        mListener.reset();
172        assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_1));
173        assertEquals(0, mTarget.getMailboxListStatusForTest(ACCOUNT_1).getLastRefreshTime());
174
175        // Done.
176        Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass());
177        mController.mListener.updateMailboxListCallback(null, ACCOUNT_1, 100);
178
179        assertTrue(mListener.mCalledOnRefreshStatusChanged);
180        assertFalse(mListener.mCalledOnConnectionError);
181        assertEquals(ACCOUNT_1, mListener.mAccountId);
182        assertEquals(-1, mListener.mMailboxId);
183        mListener.reset();
184        assertFalse(mTarget.isMailboxListRefreshing(ACCOUNT_1));
185        assertEquals(mClock.mTime, mTarget.getMailboxListStatusForTest(ACCOUNT_1)
186                .getLastRefreshTime());
187
188        // Check "any" method.
189        assertTrue(mTarget.isRefreshingAnyMailboxListForTest()); // still refreshing account 2
190
191        // Refreshing for account 2...
192        mClock.advance();
193
194        mController.mListener.updateMailboxListCallback(null, ACCOUNT_2, 0);
195
196        assertTrue(mListener.mCalledOnRefreshStatusChanged);
197        assertFalse(mListener.mCalledOnConnectionError);
198        assertEquals(ACCOUNT_2, mListener.mAccountId);
199        assertEquals(-1, mListener.mMailboxId);
200        mListener.reset();
201        assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_2));
202        assertEquals(0, mTarget.getMailboxListStatusForTest(ACCOUNT_2).getLastRefreshTime());
203
204        // Done with exception.
205        mController.mListener.updateMailboxListCallback(EXCEPTION, ACCOUNT_2, 0);
206
207        assertTrue(mListener.mCalledOnRefreshStatusChanged);
208        assertTrue(mListener.mCalledOnConnectionError);
209        assertEquals(ACCOUNT_2, mListener.mAccountId);
210        assertEquals(-1, mListener.mMailboxId);
211        assertEquals(MessagingExceptionStrings.getErrorString(mContext, EXCEPTION),
212                mListener.mMessage);
213        mListener.reset();
214        assertFalse(mTarget.isMailboxListRefreshing(ACCOUNT_2));
215        assertEquals(mClock.mTime, mTarget.getMailboxListStatusForTest(ACCOUNT_2)
216                .getLastRefreshTime());
217
218        // Check "any" method.
219        assertFalse(mTarget.isRefreshingAnyMailboxListForTest());
220    }
221
222    public void testRefreshMessageList() {
223        // request refresh mailbox 1
224        assertTrue(mTarget.refreshMessageList(ACCOUNT_1, MAILBOX_1, false));
225
226        assertTrue(mListener.mCalledOnRefreshStatusChanged);
227        assertFalse(mListener.mCalledOnConnectionError);
228        assertEquals(ACCOUNT_1, mListener.mAccountId);
229        assertEquals(MAILBOX_1, mListener.mMailboxId);
230        mListener.reset();
231        assertTrue(mController.mCalledUpdateMailbox);
232        assertEquals(ACCOUNT_1, mController.mAccountId);
233        assertEquals(MAILBOX_1, mController.mMailboxId);
234        mController.reset();
235        assertTrue(mTarget.isMessageListRefreshing(MAILBOX_1));
236        assertTrue(mTarget.isRefreshingAnyMessageListForTest());
237
238        // Request again -- shouldn't be accepted.
239        assertFalse(mTarget.refreshMessageList(ACCOUNT_1, MAILBOX_1, false));
240
241        assertFalse(mListener.mCalledOnRefreshStatusChanged);
242        assertFalse(mListener.mCalledOnConnectionError);
243        mListener.reset();
244        assertFalse(mController.mCalledUpdateMailbox);
245        mController.reset();
246
247        // request refresh mailbox 2
248        assertTrue(mTarget.refreshMessageList(ACCOUNT_2, MAILBOX_2, false));
249
250        assertTrue(mListener.mCalledOnRefreshStatusChanged);
251        assertFalse(mListener.mCalledOnConnectionError);
252        assertEquals(ACCOUNT_2, mListener.mAccountId);
253        assertEquals(MAILBOX_2, mListener.mMailboxId);
254        mListener.reset();
255        assertTrue(mController.mCalledUpdateMailbox);
256        assertEquals(ACCOUNT_2, mController.mAccountId);
257        assertEquals(MAILBOX_2, mController.mMailboxId);
258        mController.reset();
259        assertTrue(mTarget.isMessageListRefreshing(MAILBOX_2));
260        assertTrue(mTarget.isRefreshingAnyMessageListForTest());
261
262        // Refreshing mailbox 1...
263        mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 0, 0, null);
264
265        assertTrue(mListener.mCalledOnRefreshStatusChanged);
266        assertFalse(mListener.mCalledOnConnectionError);
267        assertEquals(ACCOUNT_1, mListener.mAccountId);
268        assertEquals(MAILBOX_1, mListener.mMailboxId);
269        mListener.reset();
270        assertTrue(mTarget.isMessageListRefreshing(MAILBOX_1));
271        assertEquals(0, mTarget.getMessageListStatusForTest(MAILBOX_1).getLastRefreshTime());
272
273        // Done.
274        Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass());
275        mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 100, 0, null);
276
277        assertTrue(mListener.mCalledOnRefreshStatusChanged);
278        assertFalse(mListener.mCalledOnConnectionError);
279        assertEquals(ACCOUNT_1, mListener.mAccountId);
280        assertEquals(MAILBOX_1, mListener.mMailboxId);
281        mListener.reset();
282        assertFalse(mTarget.isMessageListRefreshing(MAILBOX_1));
283        assertEquals(mClock.mTime, mTarget.getMessageListStatusForTest(MAILBOX_1)
284                .getLastRefreshTime());
285
286        // Check "any" method.
287        assertTrue(mTarget.isRefreshingAnyMessageListForTest()); // still refreshing mailbox 2
288
289        // Refreshing mailbox 2...
290        mClock.advance();
291
292        mController.mListener.updateMailboxCallback(null, ACCOUNT_2, MAILBOX_2, 0, 0, null);
293
294        assertTrue(mListener.mCalledOnRefreshStatusChanged);
295        assertFalse(mListener.mCalledOnConnectionError);
296        assertEquals(ACCOUNT_2, mListener.mAccountId);
297        assertEquals(MAILBOX_2, mListener.mMailboxId);
298        mListener.reset();
299        assertTrue(mTarget.isMessageListRefreshing(MAILBOX_2));
300        assertEquals(0, mTarget.getMessageListStatusForTest(MAILBOX_2).getLastRefreshTime());
301
302        // Done with exception.
303        mController.mListener.updateMailboxCallback(EXCEPTION, ACCOUNT_2, MAILBOX_2, 0, 0, null);
304
305        assertTrue(mListener.mCalledOnRefreshStatusChanged);
306        assertTrue(mListener.mCalledOnConnectionError);
307        assertEquals(ACCOUNT_2, mListener.mAccountId);
308        assertEquals(MAILBOX_2, mListener.mMailboxId);
309        assertEquals(MessagingExceptionStrings.getErrorString(mContext, EXCEPTION),
310                mListener.mMessage);
311        mListener.reset();
312        assertFalse(mTarget.isMessageListRefreshing(MAILBOX_2));
313        assertEquals(mClock.mTime, mTarget.getMessageListStatusForTest(MAILBOX_2)
314                .getLastRefreshTime());
315
316        // Check "any" method.
317        assertFalse(mTarget.isRefreshingAnyMessageListForTest());
318    }
319
320    public void testSendPendingMessages() {
321        // request sending for account 1
322        assertTrue(mTarget.sendPendingMessages(ACCOUNT_1));
323
324        assertTrue(mListener.mCalledOnRefreshStatusChanged);
325        assertFalse(mListener.mCalledOnConnectionError);
326        assertEquals(ACCOUNT_1, mListener.mAccountId);
327        assertEquals(-1, mListener.mMailboxId);
328        mListener.reset();
329        assertTrue(mController.mCalledSendPendingMessages);
330        assertEquals(ACCOUNT_1, mController.mAccountId);
331        assertEquals(-1, mController.mMailboxId);
332        mController.reset();
333
334        // request sending for account 2
335        assertTrue(mTarget.sendPendingMessages(ACCOUNT_2));
336
337        assertFalse(mListener.mCalledOnConnectionError);
338        assertEquals(ACCOUNT_2, mListener.mAccountId);
339        assertEquals(-1, mListener.mMailboxId);
340        mListener.reset();
341        assertTrue(mController.mCalledSendPendingMessages);
342        assertEquals(ACCOUNT_2, mController.mAccountId);
343        assertEquals(-1, mController.mMailboxId);
344        mController.reset();
345
346        // Sending start for account 1...
347        // batch send start.  (message id == -1, progress == 0)
348        mController.mListener.sendMailCallback(null, ACCOUNT_1, -1, 0);
349
350        assertFalse(mListener.mCalledOnConnectionError);
351        mListener.reset();
352
353        // Per message callback
354        mController.mListener.sendMailCallback(null, ACCOUNT_1, 100, 0);
355        mController.mListener.sendMailCallback(null, ACCOUNT_1, 101, 0);
356
357        assertFalse(mListener.mCalledOnConnectionError);
358        mListener.reset();
359
360        // Exception -- first error will be reported.
361        mController.mListener.sendMailCallback(EXCEPTION, ACCOUNT_1, 102, 0);
362
363        assertTrue(mListener.mCalledOnConnectionError);
364        assertEquals(MessagingExceptionStrings.getErrorString(mContext, EXCEPTION),
365                mListener.mMessage);
366        mListener.reset();
367
368        // Exception again -- no more error callbacks
369        mController.mListener.sendMailCallback(null, ACCOUNT_1, 103, 0);
370        mController.mListener.sendMailCallback(EXCEPTION, ACCOUNT_1, 104, 0);
371
372        assertFalse(mListener.mCalledOnConnectionError);
373        mListener.reset();
374
375        // Done.
376        Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass());
377        mController.mListener.sendMailCallback(null, ACCOUNT_1, -1, 100);
378
379        assertFalse(mListener.mCalledOnConnectionError);
380        mListener.reset();
381    }
382
383    public void testSendPendingMessagesForAllAccounts() throws Throwable {
384        Account acct1 = ProviderTestUtils.setupAccount("acct1", true, mProviderContext);
385        Account acct2 = ProviderTestUtils.setupAccount("acct2", true, mProviderContext);
386
387        // AsyncTask needs to be created on the UI thread.
388        runTestOnUiThread(new Runnable() {
389            @Override
390            public void run() {
391                mTarget.sendPendingMessagesForAllAccounts();
392            }
393        });
394
395        // sendPendingMessagesForAllAccounts uses Utility.ForEachAccount, which has it's own test,
396        // so we don't really have to check everything.
397        // Here, we just check if sendPendingMessages() has been called at least for once,
398        // which is a enough check.
399        TestUtils.waitUntil(new TestUtils.Condition() {
400            @Override
401            public boolean isMet() {
402                // The write to this is done on the UI thread, but we're checking it here
403                // on the test thread, so mCalledSendPendingMessages needs to be volatile.
404                return mController.mCalledSendPendingMessages;
405            }
406        }, WAIT_UNTIL_TIMEOUT_SECONDS);
407    }
408
409    public void testLoadMoreMessages() {
410        final long ACCOUNT_ID = 123;
411        final long MAILBOX_ID = 456;
412
413        mTarget.loadMoreMessages(ACCOUNT_ID, MAILBOX_ID);
414
415        assertTrue(mController.mCalledLoadMoreMessages);
416        assertEquals(mController.mMailboxId, MAILBOX_ID);
417        assertFalse(mController.mCalledUpdateMailbox);
418    }
419
420    // volatile is necessary for testSendPendingMessagesForAllAccounts().
421    // (Not all of them are actually necessary, but added for consistency.)
422    private static class MockController extends Controller {
423        public volatile long mAccountId = -1;
424        public volatile long mMailboxId = -1;
425        public volatile boolean mCalledSendPendingMessages;
426        public volatile boolean mCalledUpdateMailbox;
427        public volatile boolean mCalledUpdateMailboxList;
428        public volatile boolean mCalledLoadMoreMessages;
429        public volatile Result mListener;
430
431        protected MockController(Context context) {
432            super(context);
433        }
434
435        public void reset() {
436            mAccountId = -1;
437            mMailboxId = -1;
438            mCalledSendPendingMessages = false;
439            mCalledUpdateMailbox = false;
440            mCalledUpdateMailboxList = false;
441        }
442
443        @Override
444        public void sendPendingMessages(long accountId) {
445            mCalledSendPendingMessages = true;
446            mAccountId = accountId;
447        }
448
449        @Override
450        public void updateMailbox(long accountId, long mailboxId, boolean userRequest) {
451            mCalledUpdateMailbox = true;
452            mAccountId = accountId;
453            mMailboxId = mailboxId;
454        }
455
456        @Override
457        public void updateMailboxList(long accountId) {
458            mCalledUpdateMailboxList = true;
459            mAccountId = accountId;
460        }
461
462        @Override
463        public void loadMoreMessages(long mailboxId) {
464            mCalledLoadMoreMessages = true;
465            mAccountId = -1;
466            mMailboxId = mailboxId;
467        }
468
469        @Override
470        public void addResultCallback(Result listener) {
471            Assert.assertTrue(mListener == null);
472            mListener = listener;
473
474            // Let it call listener.setRegistered(). Otherwise callbacks won't fire.
475            super.addResultCallback(listener);
476        }
477    }
478
479    private static class RefreshListener implements RefreshManager.Listener {
480        public long mAccountId = -1;
481        public long mMailboxId = -1;
482        public String mMessage;
483        public boolean mCalledOnConnectionError;
484        public boolean mCalledOnRefreshStatusChanged;
485
486        public void reset() {
487            mAccountId = -1;
488            mMailboxId = -1;
489            mMessage = null;
490            mCalledOnConnectionError = false;
491            mCalledOnRefreshStatusChanged = false;
492        }
493
494        @Override
495        public void onRefreshStatusChanged(long accountId, long mailboxId) {
496            mAccountId = accountId;
497            mMailboxId = mailboxId;
498            mCalledOnRefreshStatusChanged = true;
499        }
500
501        @Override
502        public void onMessagingError(long accountId, long mailboxId, String message) {
503            mAccountId = accountId;
504            mMailboxId = mailboxId;
505            mMessage = message;
506            mCalledOnConnectionError = true;
507        }
508    }
509}
510