Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2013 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 android.provider.cts;
     18 
     19 import static android.provider.cts.contacts.DatabaseAsserts.ContactIdPair;
     20 
     21 import android.accounts.Account;
     22 import android.accounts.AccountManager;
     23 import android.content.ContentResolver;
     24 import android.os.SystemClock;
     25 import android.provider.cts.contacts.CommonDatabaseUtils;
     26 import android.provider.cts.contacts.ContactUtil;
     27 import android.provider.cts.contacts.DataUtil;
     28 import android.provider.cts.contacts.DatabaseAsserts;
     29 import android.provider.cts.contacts.DeletedContactUtil;
     30 import android.provider.cts.contacts.RawContactUtil;
     31 import android.provider.cts.contacts.account.StaticAccountAuthenticator;
     32 import android.test.AndroidTestCase;
     33 import android.test.suitebuilder.annotation.MediumTest;
     34 
     35 import com.google.android.collect.Lists;
     36 
     37 import java.util.ArrayList;
     38 import java.util.Arrays;
     39 
     40 @MediumTest
     41 public class ContactsProvider2_AccountRemovalTest extends AndroidTestCase {
     42 
     43     private static long ASYNC_TIMEOUT_LIMIT_MS = 1000 * 60 * 1; // 3 minutes
     44     private static long SLEEP_BETWEEN_POLL_MS = 1000 * 10; // 10 seconds
     45 
     46     private static int NOT_MERGED = -1;
     47 
     48     // Not re-using StaticAcountAuthenticator.ACCOUNT_1 because this test may break
     49     // other tests running when the account is removed.  No other tests should use the following
     50     // accounts.
     51     private static final Account ACCT_1 = new Account("cp removal acct 1",
     52             StaticAccountAuthenticator.TYPE);
     53     private static final Account ACCT_2 = new Account("cp removal acct 2",
     54             StaticAccountAuthenticator.TYPE);
     55 
     56     private ContentResolver mResolver;
     57     private AccountManager mAccountManager;
     58 
     59     @Override
     60     protected void setUp() throws Exception {
     61         super.setUp();
     62         mResolver = getContext().getContentResolver();
     63         mAccountManager = AccountManager.get(getContext());
     64     }
     65 
     66     @Override
     67     protected void tearDown() throws Exception {
     68         super.tearDown();
     69     }
     70 
     71     public void testAccountRemoval_deletesContacts() {
     72         mAccountManager.addAccountExplicitly(ACCT_1, null, null);
     73         mAccountManager.addAccountExplicitly(ACCT_2, null, null);
     74         ArrayList<ContactIdPair> acc1Ids = createContacts(ACCT_1, 5);
     75         ArrayList<ContactIdPair> acc2Ids = createContacts(ACCT_2, 15);
     76 
     77         mAccountManager.removeAccount(ACCT_2, null, null);
     78         assertContactsDeletedEventually(System.currentTimeMillis(), acc2Ids);
     79 
     80         mAccountManager.removeAccount(ACCT_1, null, null);
     81         assertContactsDeletedEventually(System.currentTimeMillis(), acc1Ids);
     82     }
     83 
     84     public void testAccountRemoval_hasDeleteLogsForContacts() {
     85         mAccountManager.addAccountExplicitly(ACCT_1, null, null);
     86         mAccountManager.addAccountExplicitly(ACCT_2, null, null);
     87         ArrayList<ContactIdPair> acc1Ids = createContacts(ACCT_1, 5);
     88         ArrayList<ContactIdPair> acc2Ids = createContacts(ACCT_2, 15);
     89 
     90         long start = System.currentTimeMillis();
     91         mAccountManager.removeAccount(ACCT_2, null, null);
     92         assertContactsInDeleteLogEventually(start, acc2Ids);
     93 
     94         start = System.currentTimeMillis();
     95         mAccountManager.removeAccount(ACCT_1, null, null);
     96         assertContactsInDeleteLogEventually(start, acc1Ids);
     97     }
     98 
     99     /**
    100      * Contact has merged raw contacts from a single account.  Contact should be deleted upon
    101      * account removal.
    102      */
    103     public void testAccountRemovalWithMergedContact_deletesContacts() {
    104         mAccountManager.addAccountExplicitly(ACCT_1, null, null);
    105         ArrayList<ContactIdPair> idList = createAndAssertMergedContact(ACCT_1, ACCT_1);
    106         mAccountManager.removeAccount(ACCT_1, null, null);
    107         assertContactsDeletedEventually(System.currentTimeMillis(), idList);
    108     }
    109 
    110     /**
    111      * Contact has merged raw contacts from different accounts. Contact should not be deleted when
    112      * one account is removed.  But contact should have last updated timestamp updated.
    113      */
    114     public void testAccountRemovalWithMergedContact_doesNotDeleteContactAndTimestampUpdated() {
    115         mAccountManager.addAccountExplicitly(ACCT_1, null, null);
    116         mAccountManager.addAccountExplicitly(ACCT_2, null, null);
    117         ArrayList<ContactIdPair> idList = createAndAssertMergedContact(ACCT_1, ACCT_2);
    118         long contactId = idList.get(0).mContactId;
    119 
    120         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, contactId);
    121         long start = System.currentTimeMillis();
    122         mAccountManager.removeAccount(ACCT_1, null, null);
    123 
    124         while (ContactUtil.queryContactLastUpdatedTimestamp(mResolver, contactId) == baseTime) {
    125             assertWithinTimeoutLimit(start,
    126                     "Contact " + contactId + " last updated timestamp has not been updated.");
    127             SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
    128         }
    129         mAccountManager.removeAccount(ACCT_2, null, null);
    130     }
    131 
    132     public void testAccountRemovalWithMergedContact_hasDeleteLogsForContacts() {
    133         mAccountManager.addAccountExplicitly(ACCT_1, null, null);
    134         ArrayList<ContactIdPair> idList = createAndAssertMergedContact(ACCT_1, ACCT_1);
    135         long start = System.currentTimeMillis();
    136         mAccountManager.removeAccount(ACCT_1, null, null);
    137         assertContactsInDeleteLogEventually(start, idList);
    138     }
    139 
    140     private ArrayList<ContactIdPair> createAndAssertMergedContact(Account acct, Account acct2) {
    141         ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName(mResolver, acct,
    142                 "merge me");
    143         DataUtil.insertPhoneNumber(mResolver, ids1.mRawContactId, "555-5555");
    144 
    145         ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName(mResolver, acct2,
    146                 "merge me");
    147         DataUtil.insertPhoneNumber(mResolver, ids2.mRawContactId, "555-5555");
    148 
    149         // Check merge before continuing. Merge process is async.
    150         long mergedContactId = assertMerged(System.currentTimeMillis(), ids1.mRawContactId,
    151                 ids2.mRawContactId);
    152 
    153         // Update the contact id to the newly merged contact id.
    154         ids1.mContactId = mergedContactId;
    155         ids2.mContactId = mergedContactId;
    156 
    157         return Lists.newArrayList(ids1, ids2);
    158     }
    159 
    160     private long assertMerged(long start, long rawContactId, long rawContactId2) {
    161         long contactId = NOT_MERGED;
    162         while (contactId == NOT_MERGED) {
    163             assertWithinTimeoutLimit(start,
    164                     "Raw contact " + rawContactId + " and " + rawContactId2 + " are not merged.");
    165 
    166             SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
    167             contactId = checkMerged(rawContactId, rawContactId2);
    168         }
    169         return contactId;
    170     }
    171 
    172     private long checkMerged(long rawContactId, long rawContactId2) {
    173         long contactId = RawContactUtil.queryContactIdByRawContactId(mResolver, rawContactId);
    174         long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver, rawContactId2);
    175         if (contactId == contactId2) {
    176             return contactId;
    177         }
    178         return NOT_MERGED;
    179     }
    180 
    181     private void assertContactsInDeleteLogEventually(long start, ArrayList<ContactIdPair> idList) {
    182         // Can not use newArrayList() because the version that accepts size is missing.
    183         ArrayList<ContactIdPair> remaining = new ArrayList<ContactIdPair>(idList.size());
    184         remaining.addAll(idList);
    185         while (!remaining.isEmpty()) {
    186             // Account cleanup is asynchronous, wait a bit before checking.
    187             SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
    188             assertWithinTimeoutLimit(start, "Contacts " + Arrays.toString(remaining.toArray()) +
    189                     " are not in delete log after account removal.");
    190 
    191             // Need a second list to remove since we can't remove from the list while iterating.
    192             ArrayList<ContactIdPair> toBeRemoved = Lists.newArrayList();
    193             for (ContactIdPair ids : remaining) {
    194                 long deletedTime = DeletedContactUtil.queryDeletedTimestampForContactId(mResolver,
    195                         ids.mContactId);
    196                 if (deletedTime != CommonDatabaseUtils.NOT_FOUND) {
    197                     toBeRemoved.add(ids);
    198                     assertTrue("Deleted contact was found in delete log but insert time is before"
    199                             + " start time", deletedTime > start);
    200                 }
    201             }
    202             remaining.removeAll(toBeRemoved);
    203         }
    204 
    205         // All contacts in delete log.  Pass.
    206     }
    207 
    208     /**
    209      * Polls every so often to see if all contacts have been deleted.  If not deleted in the
    210      * pre-defined threshold, fails.
    211      */
    212     private void assertContactsDeletedEventually(long start, ArrayList<ContactIdPair> idList) {
    213         // Can not use newArrayList() because the version that accepts size is missing.
    214         ArrayList<ContactIdPair> remaining = new ArrayList<ContactIdPair>(idList.size());
    215         remaining.addAll(idList);
    216         while (!remaining.isEmpty()) {
    217             // Account cleanup is asynchronous, wait a bit before checking.
    218             SystemClock.sleep(SLEEP_BETWEEN_POLL_MS);
    219             assertWithinTimeoutLimit(start, "Contacts have not been deleted after account"
    220                     + " removal.");
    221 
    222             ArrayList<ContactIdPair> toBeRemoved = Lists.newArrayList();
    223             for (ContactIdPair ids : remaining) {
    224                 if (!RawContactUtil.rawContactExistsById(mResolver, ids.mRawContactId)) {
    225                     toBeRemoved.add(ids);
    226                 }
    227             }
    228             remaining.removeAll(toBeRemoved);
    229         }
    230 
    231         // All contacts deleted.  Pass.
    232     }
    233 
    234     private void assertWithinTimeoutLimit(long start, String message) {
    235         long now = System.currentTimeMillis();
    236         long elapsed = now - start;
    237         if (elapsed > ASYNC_TIMEOUT_LIMIT_MS) {
    238             fail(elapsed + "ms has elapsed. The limit is " + ASYNC_TIMEOUT_LIMIT_MS + "ms. " +
    239                     message);
    240         }
    241     }
    242 
    243     /**
    244      * Creates a given number of contacts for an account.
    245      */
    246     private ArrayList<ContactIdPair> createContacts(Account account, int numContacts) {
    247         ArrayList<ContactIdPair> accountIds = Lists.newArrayList();
    248         for (int i = 0; i < numContacts; i++) {
    249             accountIds.add(DatabaseAsserts.assertAndCreateContact(mResolver, account));
    250         }
    251         return accountIds;
    252     }
    253 }
    254