Home | History | Annotate | Download | only in service
      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.service;
     18 
     19 import android.accounts.AccountManager;
     20 import android.content.ComponentName;
     21 import android.content.ContentResolver;
     22 import android.content.ContentUris;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.content.pm.PackageManager;
     26 
     27 import com.android.email.AccountTestCase;
     28 import com.android.email.Controller;
     29 import com.android.email.provider.AccountReconciler;
     30 import com.android.email.provider.EmailProvider;
     31 import com.android.email.provider.ProviderTestUtils;
     32 import com.android.email.service.MailService.AccountSyncReport;
     33 import com.android.emailcommon.provider.Account;
     34 import com.android.emailcommon.provider.EmailContent;
     35 import com.android.emailcommon.provider.HostAuth;
     36 
     37 import java.util.ArrayList;
     38 import java.util.HashMap;
     39 import java.util.List;
     40 
     41 /**
     42  * Tests of the Email provider.
     43  *
     44  * You can run this entire test case with:
     45  *   runtest -c com.android.email.service.MailServiceTests email
     46  */
     47 public class MailServiceTests extends AccountTestCase {
     48 
     49     EmailProvider mProvider;
     50     Context mMockContext;
     51 
     52     public MailServiceTests() {
     53         super();
     54     }
     55 
     56     @Override
     57     public void setUp() throws Exception {
     58         super.setUp();
     59         PackageManager pm = getContext().getPackageManager();
     60         pm.setComponentEnabledSetting(
     61                 new ComponentName(getContext(), EasTestAuthenticatorService.class),
     62                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
     63                 PackageManager.DONT_KILL_APP);
     64         mMockContext = getMockContext();
     65         // Delete any test accounts we might have created earlier
     66         deleteTemporaryAccountManagerAccounts();
     67     }
     68 
     69     @Override
     70     public void tearDown() throws Exception {
     71         super.tearDown();
     72         // Delete any test accounts we might have created earlier
     73         deleteTemporaryAccountManagerAccounts();
     74     }
     75 
     76     /**
     77      * Confirm that the test below is functional (and non-destructive) when there are
     78      * prexisting (non-test) accounts in the account manager.
     79      */
     80     public void testTestReconcileAccounts() {
     81         Account firstAccount = null;
     82         final String TEST_USER_ACCOUNT = "__user_account_test_1";
     83         Context context = getContext();
     84         try {
     85             // Note:  Unlike calls to setupProviderAndAccountManagerAccount(), we are creating
     86             // *real* accounts here (not in the mock provider)
     87             createAccountManagerAccount(TEST_USER_ACCOUNT + TEST_ACCOUNT_SUFFIX);
     88             firstAccount = ProviderTestUtils.setupAccount(TEST_USER_ACCOUNT, true, context);
     89             // Now run the test with the "user" accounts in place
     90             testReconcileAccounts();
     91         } finally {
     92             if (firstAccount != null) {
     93                 boolean firstAccountFound = false;
     94                 // delete the provider account
     95                 context.getContentResolver().delete(firstAccount.getUri(), null, null);
     96                 // delete the account manager account
     97                 android.accounts.Account[] accountManagerAccounts = AccountManager.get(context)
     98                         .getAccountsByType(TEST_ACCOUNT_TYPE);
     99                 for (android.accounts.Account accountManagerAccount: accountManagerAccounts) {
    100                     if ((TEST_USER_ACCOUNT + TEST_ACCOUNT_SUFFIX)
    101                             .equals(accountManagerAccount.name)) {
    102                         deleteAccountManagerAccount(accountManagerAccount);
    103                         firstAccountFound = true;
    104                     }
    105                 }
    106                 assertTrue(firstAccountFound);
    107             }
    108         }
    109     }
    110 
    111     /**
    112      * Note, there is some inherent risk in this test, as it creates *real* accounts in the
    113      * system (it cannot use the mock context with the Account Manager).
    114      */
    115     public void testReconcileAccounts() {
    116         // Note that we can't use mMockContext for AccountManager interactions, as it isn't a fully
    117         // functional Context.
    118         Context context = getContext();
    119 
    120         // Capture the baseline (account manager accounts) so we can measure the changes
    121         // we're making, irrespective of the number of actual accounts, and not destroy them
    122         android.accounts.Account[] baselineAccounts =
    123             AccountManager.get(context).getAccountsByType(TEST_ACCOUNT_TYPE);
    124 
    125         // Set up three accounts, both in AccountManager and in EmailProvider
    126         Account firstAccount = setupProviderAndAccountManagerAccount(getTestAccountName("1"));
    127         setupProviderAndAccountManagerAccount(getTestAccountName("2"));
    128         setupProviderAndAccountManagerAccount(getTestAccountName("3"));
    129 
    130         // Check that they're set up properly
    131         assertEquals(3, EmailContent.count(mMockContext, Account.CONTENT_URI, null, null));
    132         android.accounts.Account[] accountManagerAccounts =
    133                 getAccountManagerAccounts(baselineAccounts);
    134         assertEquals(3, accountManagerAccounts.length);
    135 
    136         // Delete account "2" from AccountManager
    137         android.accounts.Account removedAccount =
    138             makeAccountManagerAccount(getTestAccountEmailAddress("2"));
    139         deleteAccountManagerAccount(removedAccount);
    140 
    141         // Confirm it's deleted
    142         accountManagerAccounts = getAccountManagerAccounts(baselineAccounts);
    143         assertEquals(2, accountManagerAccounts.length);
    144 
    145         // Run the reconciler
    146         ContentResolver resolver = mMockContext.getContentResolver();
    147         MailService.reconcileAccountsWithAccountManager(context,
    148                 makeExchangeServiceAccountList(), accountManagerAccounts, mMockContext);
    149 
    150         // There should now be only two EmailProvider accounts
    151         assertEquals(2, EmailContent.count(mMockContext, Account.CONTENT_URI, null, null));
    152 
    153         // Ok, now we've got two of each; let's delete a provider account
    154         resolver.delete(ContentUris.withAppendedId(Account.CONTENT_URI, firstAccount.mId),
    155                 null, null);
    156         // ...and then there was one
    157         assertEquals(1, EmailContent.count(mMockContext, Account.CONTENT_URI, null, null));
    158 
    159         // Run the reconciler
    160         MailService.reconcileAccountsWithAccountManager(context,
    161                 makeExchangeServiceAccountList(), accountManagerAccounts, mMockContext);
    162 
    163         // There should now be only one AccountManager account
    164         accountManagerAccounts = getAccountManagerAccounts(baselineAccounts);
    165         assertEquals(1, accountManagerAccounts.length);
    166         // ... and it should be account "3"
    167         assertEquals(getTestAccountEmailAddress("3"), accountManagerAccounts[0].name);
    168     }
    169 
    170     public void testReconcileDetection() {
    171         Context context = getContext();
    172         List<Account> providerAccounts;
    173         android.accounts.Account[] accountManagerAccounts;
    174 
    175         android.accounts.Account[] baselineAccounts =
    176             AccountManager.get(context).getAccountsByType(TEST_ACCOUNT_TYPE);
    177 
    178         // Empty lists match.
    179         providerAccounts = new ArrayList<Account>();
    180         accountManagerAccounts = new android.accounts.Account[0];
    181         assertFalse(AccountReconciler.accountsNeedReconciling(
    182                 context, providerAccounts, accountManagerAccounts));
    183 
    184         setupProviderAndAccountManagerAccount(getTestAccountName("1"));
    185         accountManagerAccounts = getAccountManagerAccounts(baselineAccounts);
    186         providerAccounts = makeExchangeServiceAccountList();
    187 
    188         // A single account, but empty list on the other side is detected as needing reconciliation
    189         assertTrue(AccountReconciler.accountsNeedReconciling(
    190                 context, new ArrayList<Account>(), accountManagerAccounts));
    191         assertTrue(AccountReconciler.accountsNeedReconciling(
    192                 context, providerAccounts, new android.accounts.Account[0]));
    193 
    194         // Note that no reconciliation should have happened though - we just wanted to detect it.
    195         assertEquals(1, makeExchangeServiceAccountList().size());
    196         assertEquals(1, getAccountManagerAccounts(baselineAccounts).length);
    197 
    198         // Single account matches - no reconciliation should be detected.
    199         assertFalse(AccountReconciler.accountsNeedReconciling(
    200                 context, providerAccounts, accountManagerAccounts));
    201 
    202         // Provider: 1,2,3. AccountManager: 1, 3.
    203         String username = getTestAccountName("2");
    204         ProviderTestUtils.setupAccount(getTestAccountName("2"), true, getMockContext());
    205         setupProviderAndAccountManagerAccount(getTestAccountName("3"));
    206 
    207         accountManagerAccounts = getAccountManagerAccounts(baselineAccounts);
    208         providerAccounts = makeExchangeServiceAccountList();
    209         assertTrue(AccountReconciler.accountsNeedReconciling(
    210                 context, providerAccounts, accountManagerAccounts));
    211     }
    212 
    213 
    214     /**
    215      * Lightweight subclass of the Controller class allows injection of mock context
    216      */
    217     public static class TestController extends Controller {
    218 
    219         protected TestController(Context providerContext, Context systemContext) {
    220             super(systemContext);
    221             setProviderContext(providerContext);
    222         }
    223     }
    224 
    225     /**
    226      * Create a simple HostAuth with protocol
    227      */
    228     private HostAuth setupSimpleHostAuth(String protocol) {
    229         HostAuth hostAuth = new HostAuth();
    230         hostAuth.mProtocol = protocol;
    231         return hostAuth;
    232     }
    233 
    234     /**
    235      * Initial testing on setupSyncReportsLocked, making sure that EAS accounts aren't scheduled
    236      */
    237     public void testSetupSyncReportsLocked() {
    238         // TODO Test other functionality within setupSyncReportsLocked
    239         // Setup accounts of each type, all with manual sync at different intervals
    240         Account easAccount = ProviderTestUtils.setupAccount("account1", false, mMockContext);
    241         easAccount.mHostAuthRecv = setupSimpleHostAuth("eas");
    242         easAccount.mHostAuthSend = easAccount.mHostAuthRecv;
    243         easAccount.mSyncInterval = 30;
    244         easAccount.save(mMockContext);
    245         Account imapAccount = ProviderTestUtils.setupAccount("account2", false, mMockContext);
    246         imapAccount.mHostAuthRecv = setupSimpleHostAuth("imap");
    247         imapAccount.mHostAuthSend = setupSimpleHostAuth("smtp");
    248         imapAccount.mSyncInterval = 60;
    249         imapAccount.save(mMockContext);
    250         Account pop3Account = ProviderTestUtils.setupAccount("account3", false, mMockContext);
    251         pop3Account.mHostAuthRecv = setupSimpleHostAuth("pop3");
    252         pop3Account.mHostAuthSend = setupSimpleHostAuth("smtp");
    253         pop3Account.mSyncInterval = 90;
    254         pop3Account.save(mMockContext);
    255 
    256         // Setup the SyncReport's for these Accounts
    257         MailService mailService = new MailService();
    258         mailService.mController = new TestController(mMockContext, getContext());
    259         try {
    260             mailService.setupSyncReportsLocked(MailService.SYNC_REPORTS_RESET, mMockContext);
    261 
    262             // Get back the map created by MailService
    263             HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports;
    264             synchronized (syncReportMap) {
    265                 // Check the SyncReport's for correctness of sync interval
    266                 AccountSyncReport syncReport = syncReportMap.get(easAccount.mId);
    267                 assertNotNull(syncReport);
    268                 // EAS sync interval should have been changed to "never"
    269                 assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval);
    270                 syncReport = syncReportMap.get(imapAccount.mId);
    271                 assertNotNull(syncReport);
    272                 assertEquals(60, syncReport.syncInterval);
    273                 syncReport = syncReportMap.get(pop3Account.mId);
    274                 assertNotNull(syncReport);
    275                 assertEquals(90, syncReport.syncInterval);
    276                 // Change the EAS account to push
    277                 ContentValues cv = new ContentValues();
    278                 cv.put(Account.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
    279                 easAccount.update(mMockContext, cv);
    280                 syncReportMap.clear();
    281                 mailService.setupSyncReportsLocked(easAccount.mId, mMockContext);
    282                 syncReport = syncReportMap.get(easAccount.mId);
    283                 assertNotNull(syncReport);
    284                 // EAS sync interval should be "never" in this case as well
    285                 assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval);
    286             }
    287         } finally {
    288             mailService.mController.cleanupForTest();
    289         }
    290     }
    291 
    292     /**
    293      * Test that setupSyncReports will skip over poorly-formed accounts which can be left
    294      * over after unit tests.
    295      */
    296     public void testSetupSyncReportsWithBadAccounts() {
    297         // Setup accounts that trigger each skip-over case
    298         // 1: no email address
    299         Account account1 = ProviderTestUtils.setupAccount("account1", false, mMockContext);
    300         account1.mHostAuthRecv = setupSimpleHostAuth("imap");
    301         account1.mHostAuthSend = setupSimpleHostAuth("smtp");
    302         account1.mSyncInterval = 30;
    303         account1.mEmailAddress = null;
    304         account1.save(mMockContext);
    305         // 2: no receiver hostauth
    306         Account account2 = ProviderTestUtils.setupAccount("account2", false, mMockContext);
    307         account2.mHostAuthRecv = null;
    308         account2.mHostAuthSend = setupSimpleHostAuth("smtp");
    309         account2.mSyncInterval = 30;
    310         account2.save(mMockContext);
    311         // 3: no sender hostauth
    312         Account account3 = ProviderTestUtils.setupAccount("account3", false, mMockContext);
    313         account3.mHostAuthRecv = setupSimpleHostAuth("imap");
    314         account3.mHostAuthSend = null;
    315         account3.mSyncInterval = 30;
    316         account3.save(mMockContext);
    317 
    318         // Setup the SyncReport's for these Accounts
    319         MailService mailService = new MailService();
    320         mailService.mController = new TestController(mMockContext, getContext());
    321         try {
    322             mailService.setupSyncReportsLocked(MailService.SYNC_REPORTS_RESET, mMockContext);
    323             // Get back the map created by MailService - it should be empty
    324             HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports;
    325             assertEquals(0, syncReportMap.size());
    326         } finally {
    327             mailService.mController.cleanupForTest();
    328         }
    329 
    330     }
    331 }
    332