/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.email; import com.android.email.provider.ProviderTestUtils; import com.android.emailcommon.Logging; import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.provider.Account; import android.content.Context; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import junit.framework.Assert; @LargeTest public class RefreshManagerTest extends InstrumentationTestCase { private static final int WAIT_UNTIL_TIMEOUT_SECONDS = 15; private MockClock mClock; private MockController mController; private RefreshManager mTarget; private RefreshListener mListener; private Context mContext; // Isolated Context for providers. private Context mProviderContext; private static final MessagingException EXCEPTION = new MessagingException("test"); // Looks silly, but it'll make it more readable. private static final long ACCOUNT_1 = 1; private static final long ACCOUNT_2 = 2; private static final long MAILBOX_1 = 3; private static final long MAILBOX_2 = 4; @Override protected void setUp() throws Exception { super.setUp(); mClock = new MockClock(); mContext = getInstrumentation().getTargetContext(); mController = new MockController(mContext); mListener = new RefreshListener(); mProviderContext = DBTestHelper.ProviderContextSetupHelper.getProviderContext(mContext); mTarget = new RefreshManager(mProviderContext, mController, mClock, null); mTarget.registerListener(mListener); } @Override protected void tearDown() throws Exception { super.tearDown(); mController.cleanupForTest(); } public void testRegisterUnregisterListener() { // mListener is already registered assertEquals(1, mTarget.getListenersForTest().size()); mTarget.unregisterListener(mListener); assertEquals(0, mTarget.getListenersForTest().size()); } public void testRefreshStatus() { RefreshManager.Status s = new RefreshManager.Status(); assertFalse(s.isRefreshing()); assertTrue(s.canRefresh()); assertEquals(0, s.getLastRefreshTime()); // Request refresh s.onRefreshRequested(); assertTrue(s.isRefreshing()); assertFalse(s.canRefresh()); assertEquals(0, s.getLastRefreshTime()); // Refresh start s.onCallback(null, 0, mClock); assertTrue(s.isRefreshing()); assertFalse(s.canRefresh()); assertEquals(0, s.getLastRefreshTime()); // Refresh 50% done -- nothing changes s.onCallback(null, 50, mClock); assertTrue(s.isRefreshing()); assertFalse(s.canRefresh()); assertEquals(0, s.getLastRefreshTime()); // Refresh finish s.onCallback(null, 100, mClock); assertFalse(s.isRefreshing()); assertTrue(s.canRefresh()); assertEquals(mClock.mTime, s.getLastRefreshTime()); // Refresh start without request s.onCallback(null, 0, mClock); assertTrue(s.isRefreshing()); assertFalse(s.canRefresh()); assertEquals(mClock.mTime, s.getLastRefreshTime()); mClock.advance(); // Refresh finish with error. s.onCallback(EXCEPTION, 0, mClock); assertFalse(s.isRefreshing()); assertTrue(s.canRefresh()); assertEquals(mClock.mTime, s.getLastRefreshTime()); } public void testRefreshMailboxList() { // request refresh for account 1 assertTrue(mTarget.refreshMailboxList(ACCOUNT_1)); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertTrue(mController.mCalledUpdateMailboxList); assertEquals(ACCOUNT_1, mController.mAccountId); assertEquals(-1, mController.mMailboxId); mController.reset(); assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_1)); assertTrue(mTarget.isRefreshingAnyMailboxListForTest()); // Request again -- shouldn't be accepted. assertFalse(mTarget.refreshMailboxList(ACCOUNT_1)); assertFalse(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); mListener.reset(); assertFalse(mController.mCalledUpdateMailboxList); mController.reset(); // request refresh for account 2 assertTrue(mTarget.refreshMailboxList(ACCOUNT_2)); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertTrue(mController.mCalledUpdateMailboxList); assertEquals(ACCOUNT_2, mController.mAccountId); assertEquals(-1, mController.mMailboxId); mController.reset(); assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_2)); assertTrue(mTarget.isRefreshingAnyMailboxListForTest()); // Refreshing for account 1... mController.mListener.updateMailboxListCallback(null, ACCOUNT_1, 0); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_1)); assertEquals(0, mTarget.getMailboxListStatusForTest(ACCOUNT_1).getLastRefreshTime()); // Done. Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass()); mController.mListener.updateMailboxListCallback(null, ACCOUNT_1, 100); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertFalse(mTarget.isMailboxListRefreshing(ACCOUNT_1)); assertEquals(mClock.mTime, mTarget.getMailboxListStatusForTest(ACCOUNT_1) .getLastRefreshTime()); // Check "any" method. assertTrue(mTarget.isRefreshingAnyMailboxListForTest()); // still refreshing account 2 // Refreshing for account 2... mClock.advance(); mController.mListener.updateMailboxListCallback(null, ACCOUNT_2, 0); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertTrue(mTarget.isMailboxListRefreshing(ACCOUNT_2)); assertEquals(0, mTarget.getMailboxListStatusForTest(ACCOUNT_2).getLastRefreshTime()); // Done with exception. mController.mListener.updateMailboxListCallback(EXCEPTION, ACCOUNT_2, 0); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertTrue(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); assertEquals(MessagingExceptionStrings.getErrorString(mContext, EXCEPTION), mListener.mMessage); mListener.reset(); assertFalse(mTarget.isMailboxListRefreshing(ACCOUNT_2)); assertEquals(mClock.mTime, mTarget.getMailboxListStatusForTest(ACCOUNT_2) .getLastRefreshTime()); // Check "any" method. assertFalse(mTarget.isRefreshingAnyMailboxListForTest()); } public void testRefreshMessageList() { // request refresh mailbox 1 assertTrue(mTarget.refreshMessageList(ACCOUNT_1, MAILBOX_1, false)); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(MAILBOX_1, mListener.mMailboxId); mListener.reset(); assertTrue(mController.mCalledUpdateMailbox); assertEquals(ACCOUNT_1, mController.mAccountId); assertEquals(MAILBOX_1, mController.mMailboxId); mController.reset(); assertTrue(mTarget.isMessageListRefreshing(MAILBOX_1)); assertTrue(mTarget.isRefreshingAnyMessageListForTest()); // Request again -- shouldn't be accepted. assertFalse(mTarget.refreshMessageList(ACCOUNT_1, MAILBOX_1, false)); assertFalse(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); mListener.reset(); assertFalse(mController.mCalledUpdateMailbox); mController.reset(); // request refresh mailbox 2 assertTrue(mTarget.refreshMessageList(ACCOUNT_2, MAILBOX_2, false)); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(MAILBOX_2, mListener.mMailboxId); mListener.reset(); assertTrue(mController.mCalledUpdateMailbox); assertEquals(ACCOUNT_2, mController.mAccountId); assertEquals(MAILBOX_2, mController.mMailboxId); mController.reset(); assertTrue(mTarget.isMessageListRefreshing(MAILBOX_2)); assertTrue(mTarget.isRefreshingAnyMessageListForTest()); // Refreshing mailbox 1... mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 0, 0, null); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(MAILBOX_1, mListener.mMailboxId); mListener.reset(); assertTrue(mTarget.isMessageListRefreshing(MAILBOX_1)); assertEquals(0, mTarget.getMessageListStatusForTest(MAILBOX_1).getLastRefreshTime()); // Done. Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass()); mController.mListener.updateMailboxCallback(null, ACCOUNT_1, MAILBOX_1, 100, 0, null); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(MAILBOX_1, mListener.mMailboxId); mListener.reset(); assertFalse(mTarget.isMessageListRefreshing(MAILBOX_1)); assertEquals(mClock.mTime, mTarget.getMessageListStatusForTest(MAILBOX_1) .getLastRefreshTime()); // Check "any" method. assertTrue(mTarget.isRefreshingAnyMessageListForTest()); // still refreshing mailbox 2 // Refreshing mailbox 2... mClock.advance(); mController.mListener.updateMailboxCallback(null, ACCOUNT_2, MAILBOX_2, 0, 0, null); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(MAILBOX_2, mListener.mMailboxId); mListener.reset(); assertTrue(mTarget.isMessageListRefreshing(MAILBOX_2)); assertEquals(0, mTarget.getMessageListStatusForTest(MAILBOX_2).getLastRefreshTime()); // Done with exception. mController.mListener.updateMailboxCallback(EXCEPTION, ACCOUNT_2, MAILBOX_2, 0, 0, null); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertTrue(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(MAILBOX_2, mListener.mMailboxId); assertEquals(MessagingExceptionStrings.getErrorString(mContext, EXCEPTION), mListener.mMessage); mListener.reset(); assertFalse(mTarget.isMessageListRefreshing(MAILBOX_2)); assertEquals(mClock.mTime, mTarget.getMessageListStatusForTest(MAILBOX_2) .getLastRefreshTime()); // Check "any" method. assertFalse(mTarget.isRefreshingAnyMessageListForTest()); } public void testSendPendingMessages() { // request sending for account 1 assertTrue(mTarget.sendPendingMessages(ACCOUNT_1)); assertTrue(mListener.mCalledOnRefreshStatusChanged); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_1, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertTrue(mController.mCalledSendPendingMessages); assertEquals(ACCOUNT_1, mController.mAccountId); assertEquals(-1, mController.mMailboxId); mController.reset(); // request sending for account 2 assertTrue(mTarget.sendPendingMessages(ACCOUNT_2)); assertFalse(mListener.mCalledOnConnectionError); assertEquals(ACCOUNT_2, mListener.mAccountId); assertEquals(-1, mListener.mMailboxId); mListener.reset(); assertTrue(mController.mCalledSendPendingMessages); assertEquals(ACCOUNT_2, mController.mAccountId); assertEquals(-1, mController.mMailboxId); mController.reset(); // Sending start for account 1... // batch send start. (message id == -1, progress == 0) mController.mListener.sendMailCallback(null, ACCOUNT_1, -1, 0); assertFalse(mListener.mCalledOnConnectionError); mListener.reset(); // Per message callback mController.mListener.sendMailCallback(null, ACCOUNT_1, 100, 0); mController.mListener.sendMailCallback(null, ACCOUNT_1, 101, 0); assertFalse(mListener.mCalledOnConnectionError); mListener.reset(); // Exception -- first error will be reported. mController.mListener.sendMailCallback(EXCEPTION, ACCOUNT_1, 102, 0); assertTrue(mListener.mCalledOnConnectionError); assertEquals(MessagingExceptionStrings.getErrorString(mContext, EXCEPTION), mListener.mMessage); mListener.reset(); // Exception again -- no more error callbacks mController.mListener.sendMailCallback(null, ACCOUNT_1, 103, 0); mController.mListener.sendMailCallback(EXCEPTION, ACCOUNT_1, 104, 0); assertFalse(mListener.mCalledOnConnectionError); mListener.reset(); // Done. Log.w(Logging.LOG_TAG, "" + mController.mListener.getClass()); mController.mListener.sendMailCallback(null, ACCOUNT_1, -1, 100); assertFalse(mListener.mCalledOnConnectionError); mListener.reset(); } public void testSendPendingMessagesForAllAccounts() throws Throwable { Account acct1 = ProviderTestUtils.setupAccount("acct1", true, mProviderContext); Account acct2 = ProviderTestUtils.setupAccount("acct2", true, mProviderContext); // AsyncTask needs to be created on the UI thread. runTestOnUiThread(new Runnable() { @Override public void run() { mTarget.sendPendingMessagesForAllAccounts(); } }); // sendPendingMessagesForAllAccounts uses Utility.ForEachAccount, which has it's own test, // so we don't really have to check everything. // Here, we just check if sendPendingMessages() has been called at least for once, // which is a enough check. TestUtils.waitUntil(new TestUtils.Condition() { @Override public boolean isMet() { // The write to this is done on the UI thread, but we're checking it here // on the test thread, so mCalledSendPendingMessages needs to be volatile. return mController.mCalledSendPendingMessages; } }, WAIT_UNTIL_TIMEOUT_SECONDS); } public void testLoadMoreMessages() { final long ACCOUNT_ID = 123; final long MAILBOX_ID = 456; mTarget.loadMoreMessages(ACCOUNT_ID, MAILBOX_ID); assertTrue(mController.mCalledLoadMoreMessages); assertEquals(mController.mMailboxId, MAILBOX_ID); assertFalse(mController.mCalledUpdateMailbox); } // volatile is necessary for testSendPendingMessagesForAllAccounts(). // (Not all of them are actually necessary, but added for consistency.) private static class MockController extends Controller { public volatile long mAccountId = -1; public volatile long mMailboxId = -1; public volatile boolean mCalledSendPendingMessages; public volatile boolean mCalledUpdateMailbox; public volatile boolean mCalledUpdateMailboxList; public volatile boolean mCalledLoadMoreMessages; public volatile Result mListener; protected MockController(Context context) { super(context); } public void reset() { mAccountId = -1; mMailboxId = -1; mCalledSendPendingMessages = false; mCalledUpdateMailbox = false; mCalledUpdateMailboxList = false; } @Override public void sendPendingMessages(long accountId) { mCalledSendPendingMessages = true; mAccountId = accountId; } @Override public void updateMailbox(long accountId, long mailboxId, boolean userRequest) { mCalledUpdateMailbox = true; mAccountId = accountId; mMailboxId = mailboxId; } @Override public void updateMailboxList(long accountId) { mCalledUpdateMailboxList = true; mAccountId = accountId; } @Override public void loadMoreMessages(long mailboxId) { mCalledLoadMoreMessages = true; mAccountId = -1; mMailboxId = mailboxId; } @Override public void addResultCallback(Result listener) { Assert.assertTrue(mListener == null); mListener = listener; // Let it call listener.setRegistered(). Otherwise callbacks won't fire. super.addResultCallback(listener); } } private static class RefreshListener implements RefreshManager.Listener { public long mAccountId = -1; public long mMailboxId = -1; public String mMessage; public boolean mCalledOnConnectionError; public boolean mCalledOnRefreshStatusChanged; public void reset() { mAccountId = -1; mMailboxId = -1; mMessage = null; mCalledOnConnectionError = false; mCalledOnRefreshStatusChanged = false; } @Override public void onRefreshStatusChanged(long accountId, long mailboxId) { mAccountId = accountId; mMailboxId = mailboxId; mCalledOnRefreshStatusChanged = true; } @Override public void onMessagingError(long accountId, long mailboxId, String message) { mAccountId = accountId; mMailboxId = mailboxId; mMessage = message; mCalledOnConnectionError = true; } } }