Home | History | Annotate | Download | only in exchange
      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.exchange;
     18 
     19 import android.accounts.Account;
     20 import android.accounts.AccountManager;
     21 import android.accounts.AccountManagerFuture;
     22 import android.accounts.AuthenticatorException;
     23 import android.accounts.OperationCanceledException;
     24 import android.app.NotificationManager;
     25 import android.content.ContentResolver;
     26 import android.content.Context;
     27 import android.database.Cursor;
     28 import android.provider.CalendarContract;
     29 import android.test.MoreAsserts;
     30 import android.test.suitebuilder.annotation.MediumTest;
     31 import android.text.TextUtils;
     32 import android.util.Log;
     33 
     34 import com.android.emailcommon.AccountManagerTypes;
     35 import com.android.emailcommon.Logging;
     36 import com.android.exchange.utility.ExchangeTestCase;
     37 
     38 import java.io.IOException;
     39 import java.util.ArrayList;
     40 import java.util.Arrays;
     41 import java.util.HashMap;
     42 import java.util.HashSet;
     43 @MediumTest
     44 public class CalendarSyncEnablerTest extends ExchangeTestCase {
     45 
     46     protected static final String TEST_ACCOUNT_PREFIX = "__test";
     47     protected static final String TEST_ACCOUNT_SUFFIX = "@android.com";
     48 
     49     private HashMap<Account, Boolean> origCalendarSyncStates = new HashMap<Account, Boolean>();
     50 
     51     // To make the rest of the code shorter thus more readable...
     52     private static final String EAT = AccountManagerTypes.TYPE_EXCHANGE;
     53 
     54     @Override
     55     public void setUp() throws Exception {
     56         super.setUp();
     57         // Delete any test accounts we might have created earlier
     58         deleteTemporaryAccountManagerAccounts();
     59 
     60         // Save the original calendar sync states.
     61         for (Account account : AccountManager.get(getContext()).getAccounts()) {
     62             origCalendarSyncStates.put(account,
     63                     ContentResolver.getSyncAutomatically(account, CalendarContract.AUTHORITY));
     64         }
     65     }
     66 
     67     @Override
     68     public void tearDown() throws Exception {
     69         super.tearDown();
     70         // Delete any test accounts we might have created earlier
     71         deleteTemporaryAccountManagerAccounts();
     72 
     73         // Restore the original calendar sync states.
     74         // Note we restore only for Exchange accounts.
     75         // Other accounts should remain intact throughout the tests.  Plus we don't know if the
     76         // Calendar.AUTHORITY is supported by other types of accounts.
     77         for (Account account : getExchangeAccounts()) {
     78             Boolean state = origCalendarSyncStates.get(account);
     79             if (state == null) continue; // Shouldn't happen, but just in case.
     80 
     81             ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, state);
     82         }
     83     }
     84 
     85     public void testEnableEasCalendarSync() {
     86         final Account[] baseAccounts = getExchangeAccounts();
     87 
     88         String a1 = getTestAccountEmailAddress("1");
     89         String a2 = getTestAccountEmailAddress("2");
     90 
     91         // 1. Test with 1 account
     92 
     93         CalendarSyncEnabler enabler = new CalendarSyncEnabler(getContext());
     94 
     95         // Add exchange accounts
     96         createAccountManagerAccount(a1);
     97 
     98         String emailAddresses = enabler.enableEasCalendarSyncInternalForTest();
     99 
    100         // Verify
    101         verifyCalendarSyncState();
    102 
    103         // There seems to be no good way to examine the contents of Notification, so let's verify
    104         // we at least (tried to) show the correct email addresses.
    105         checkNotificationEmailAddresses(emailAddresses, baseAccounts, a1);
    106 
    107         // Delete added account.
    108         deleteTemporaryAccountManagerAccounts();
    109 
    110         // 2. Test with 2 accounts
    111         enabler = new CalendarSyncEnabler(getContext());
    112 
    113         // Add exchange accounts
    114         createAccountManagerAccount(a1);
    115         createAccountManagerAccount(a2);
    116 
    117         emailAddresses = enabler.enableEasCalendarSyncInternalForTest();
    118 
    119         // Verify
    120         verifyCalendarSyncState();
    121 
    122         // Check
    123         checkNotificationEmailAddresses(emailAddresses, baseAccounts, a1, a2);
    124     }
    125 
    126     private static void checkNotificationEmailAddresses(String actual, Account[] baseAccounts,
    127             String... addedAddresses) {
    128         // Build and sort actual string array.
    129         final String[] actualArray = TextUtils.split(actual, " ");
    130         Arrays.sort(actualArray);
    131 
    132         // Build and sort expected string array.
    133         ArrayList<String> expected = new ArrayList<String>();
    134         for (Account account : baseAccounts) {
    135             expected.add(account.name);
    136         }
    137         for (String address : addedAddresses) {
    138             expected.add(address);
    139         }
    140         final String[] expectedArray = new String[expected.size()];
    141         expected.toArray(expectedArray);
    142         Arrays.sort(expectedArray);
    143 
    144         // Check!
    145         MoreAsserts.assertEquals(expectedArray, actualArray);
    146     }
    147 
    148     /**
    149      * For all {@link Account}, confirm that:
    150      * <ol>
    151      *   <li>Calendar sync is enabled if it's an Exchange account.<br>
    152      *       Unfortunately setSyncAutomatically() doesn't take effect immediately, so we skip this
    153      *       check for now.
    154              TODO Find a stable way to check this.
    155      *   <li>Otherwise, calendar sync state isn't changed.
    156      * </ol>
    157      */
    158     private void verifyCalendarSyncState() {
    159         // It's very unfortunate that setSyncAutomatically doesn't take effect immediately.
    160         for (Account account : AccountManager.get(getContext()).getAccounts()) {
    161             String message = "account=" + account.name + "(" + account.type + ")";
    162             boolean enabled = ContentResolver.getSyncAutomatically(account,
    163                     CalendarContract.AUTHORITY);
    164             int syncable = ContentResolver.getIsSyncable(account, CalendarContract.AUTHORITY);
    165 
    166             if (EAT.equals(account.type)) {
    167                 // Should be enabled.
    168                 // assertEquals(message, Boolean.TRUE, (Boolean) enabled);
    169                 // assertEquals(message, 1, syncable);
    170             } else {
    171                 // Shouldn't change.
    172                 assertEquals(message, origCalendarSyncStates.get(account), (Boolean) enabled);
    173             }
    174         }
    175     }
    176 
    177     public void testEnableEasCalendarSyncWithNoExchangeAccounts() {
    178         // This test can only meaningfully run when there's no exchange accounts
    179         // set up on the device.  Otherwise there'll be no difference from
    180         // testEnableEasCalendarSync.
    181         if (AccountManager.get(getContext()).getAccountsByType(EAT).length > 0) {
    182             Log.w(Logging.LOG_TAG, "testEnableEasCalendarSyncWithNoExchangeAccounts skipped:"
    183                     + " It only runs when there's no Exchange account on the device.");
    184             return;
    185         }
    186         CalendarSyncEnabler enabler = new CalendarSyncEnabler(getContext());
    187         String emailAddresses = enabler.enableEasCalendarSyncInternalForTest();
    188 
    189         // Verify (nothing should change)
    190         verifyCalendarSyncState();
    191 
    192         // No exchange accounts found.
    193         assertEquals(0, emailAddresses.length());
    194     }
    195 
    196     public void testShowNotification() {
    197         CalendarSyncEnabler enabler = new CalendarSyncEnabler(getContext());
    198 
    199         // We can't really check the result, but at least we can make sure it won't crash....
    200         enabler.showNotificationForTest("a (at) b.com");
    201 
    202         // Remove the notification.  Comment it out when you want to know how it looks like.
    203         // TODO If NotificationController supports this notification, we can just mock it out
    204         // and remove this code.
    205         ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE))
    206                 .cancel(CalendarSyncEnabler.NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED);
    207     }
    208 
    209     protected Account[] getExchangeAccounts() {
    210         return AccountManager.get(getContext()).getAccountsByType(
    211                 AccountManagerTypes.TYPE_EXCHANGE);
    212     }
    213 
    214     protected Account makeAccountManagerAccount(String username) {
    215         return new Account(username, AccountManagerTypes.TYPE_EXCHANGE);
    216     }
    217 
    218     protected void createAccountManagerAccount(String username) {
    219         final Account account = makeAccountManagerAccount(username);
    220         AccountManager.get(getContext()).addAccountExplicitly(account, "password", null);
    221     }
    222 
    223     protected com.android.emailcommon.provider.Account
    224         setupProviderAndAccountManagerAccount(String username) {
    225         // Note that setupAccount creates the email address username (at) android.com, so that's what
    226         // we need to use for the account manager
    227         createAccountManagerAccount(username + TEST_ACCOUNT_SUFFIX);
    228         return setupTestAccount(username, true);
    229     }
    230 
    231     protected ArrayList<com.android.emailcommon.provider.Account> makeExchangeServiceAccountList() {
    232         ArrayList<com.android.emailcommon.provider.Account> accountList =
    233             new ArrayList<com.android.emailcommon.provider.Account>();
    234         Cursor c = mProviderContext.getContentResolver().query(
    235                 com.android.emailcommon.provider.Account.CONTENT_URI,
    236                 com.android.emailcommon.provider.Account.CONTENT_PROJECTION, null, null, null);
    237         try {
    238             while (c.moveToNext()) {
    239                 com.android.emailcommon.provider.Account account =
    240                     new com.android.emailcommon.provider.Account();
    241                 account.restore(c);
    242                 accountList.add(account);
    243             }
    244         } finally {
    245             c.close();
    246         }
    247         return accountList;
    248     }
    249 
    250     protected void deleteAccountManagerAccount(Account account) {
    251         AccountManagerFuture<Boolean> future =
    252             AccountManager.get(getContext()).removeAccount(account, null, null);
    253         try {
    254             future.getResult();
    255         } catch (OperationCanceledException e) {
    256         } catch (AuthenticatorException e) {
    257         } catch (IOException e) {
    258         }
    259     }
    260 
    261     protected void deleteTemporaryAccountManagerAccounts() {
    262         for (Account accountManagerAccount: getExchangeAccounts()) {
    263             if (accountManagerAccount.name.startsWith(TEST_ACCOUNT_PREFIX) &&
    264                     accountManagerAccount.name.endsWith(TEST_ACCOUNT_SUFFIX)) {
    265                 deleteAccountManagerAccount(accountManagerAccount);
    266             }
    267         }
    268     }
    269 
    270     protected String getTestAccountName(String name) {
    271         return TEST_ACCOUNT_PREFIX + name;
    272     }
    273 
    274     protected String getTestAccountEmailAddress(String name) {
    275         return TEST_ACCOUNT_PREFIX + name + TEST_ACCOUNT_SUFFIX;
    276     }
    277 
    278 
    279     /**
    280      * Helper to retrieve account manager accounts *and* remove any preexisting accounts
    281      * from the list, to "hide" them from the reconciler.
    282      */
    283     protected Account[] getAccountManagerAccounts(Account[] baseline) {
    284         Account[] rawList = getExchangeAccounts();
    285         if (baseline.length == 0) {
    286             return rawList;
    287         }
    288         HashSet<Account> set = new HashSet<Account>();
    289         for (Account addAccount : rawList) {
    290             set.add(addAccount);
    291         }
    292         for (Account removeAccount : baseline) {
    293             set.remove(removeAccount);
    294         }
    295         return set.toArray(new Account[0]);
    296     }
    297 }
    298