Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a02aefa0 authored by Marcus Hagerott's avatar Marcus Hagerott
Browse files

Add tests for DeviceLocalContactsFilterProvider.

Bug 28637652
Change-Id: I7c1773610217765794ad4a3b326a07eca62def47
parent 6caf23f9
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
-keep class com.android.contacts.common.database.NoNullCursorAsyncQueryHandler { *; }
-keep class com.android.contacts.common.format.FormatUtils { *; }
-keep class com.android.contacts.common.format.TextHighlighter { *; }
-keep class com.android.contacts.common.list.ContactListFilter { *; }
-keep class com.android.contacts.common.list.ContactListItemView { *; }
-keep class com.android.contacts.common.list.ContactsSectionIndexer { *; }
-keep class com.android.contacts.common.location.CountryDetector { *; }
@@ -66,6 +67,11 @@
-keep class com.android.contacts.common.util.NameConverter { *; }
-keep class com.android.contacts.common.util.SearchUtil { *; }
-keep class com.android.contacts.common.util.SearchUtil$* { *; }
-keep class com.android.contacts.common.util.DeviceAccountFilter { *; }
-keep class com.android.contacts.common.util.DeviceAccountFilter$* { *; }
-keep class com.android.contacts.common.util.DeviceAccountPresentationValues { *; }
-keep class com.android.contacts.common.util.DeviceAccountPresentationValues$* { *; }
-keep class com.android.contacts.common.util.DeviceLocalContactsFilterProvider { *; }
-keep class com.android.contacts.ContactsApplication { *; }
-keep class com.android.contacts.ContactSaveService { *; }
-keep class com.android.contacts.ContactSaveService$* { *; }
@@ -86,11 +92,9 @@
-keep class com.google.common.collect.Multimap { *; }
-keep class com.google.common.collect.Sets { *; }

# Any class or method annotated with NeededForTesting or NeededForReflection.
-keep @com.android.contacts.common.testing.NeededForTesting class *
# Any class or method annotated with NeededForReflection.
-keep @com.android.contacts.test.NeededForReflection class *
-keepclassmembers class * {
@com.android.contacts.common.testing.NeededForTesting *;
@com.android.contacts.test.NeededForReflection *;
}

+24 −10
Original line number Diff line number Diff line
@@ -26,8 +26,11 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.Keep;
import android.support.annotation.VisibleForTesting;

import com.android.contacts.common.list.ContactListFilter;
import com.android.contacts.test.NeededForReflection;

import java.util.ArrayList;
import java.util.Collections;
@@ -74,17 +77,9 @@ public class DeviceLocalContactsFilterProvider

    @Override
    public CursorLoader onCreateLoader(int i, Bundle bundle) {
        final AccountManager accountManager = (AccountManager) mContext
                .getSystemService(Context.ACCOUNT_SERVICE);
        final Set<String> knownTypes = new HashSet<>();
        final Account[] accounts = accountManager.getAccounts();
        for (Account account : accounts) {
            if (ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) > 0) {
                knownTypes.add(account.type);
            }
        if (mKnownAccountTypes == null) {
            initKnownAccountTypes();
        }
        mKnownAccountTypes = knownTypes.toArray(new String[knownTypes.size()]);

        return new CursorLoader(mContext, getUri(), PROJECTION, getSelection(),
                getSelectionArgs(), null);
    }
@@ -128,6 +123,25 @@ public class DeviceLocalContactsFilterProvider
    public void onLoaderReset(Loader<Cursor> loader) {
    }

    @Keep
    @VisibleForTesting
    public void setKnownAccountTypes(String... accountTypes) {
        mKnownAccountTypes = accountTypes;
    }

    private void initKnownAccountTypes() {
        final AccountManager accountManager = (AccountManager) mContext
                .getSystemService(Context.ACCOUNT_SERVICE);
        final Set<String> knownTypes = new HashSet<>();
        final Account[] accounts = accountManager.getAccounts();
        for (Account account : accounts) {
            if (ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) > 0) {
                knownTypes.add(account.type);
            }
        }
        mKnownAccountTypes = knownTypes.toArray(new String[knownTypes.size()]);
    }

    private Uri getUri() {
        final Uri.Builder builder = ContactsContract.RawContacts.CONTENT_URI.buildUpon();
        if (mKnownAccountTypes == null || mKnownAccountTypes.length == 0) {
+221 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.contacts.common.util;

import android.content.ContentProvider;
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.CancellationSignal;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
import android.support.annotation.Nullable;
import android.test.LoaderTestCase;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.contacts.common.list.ContactListFilter;
import com.android.contacts.common.test.mocks.MockContentProvider;

import org.mockito.Mockito;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import static org.mockito.Mockito.when;

@SmallTest
public class DeviceLocalContactsFilterProviderTests extends LoaderTestCase {

    // Basic smoke test that just checks that it doesn't throw when loading from CP2. We don't
    // care what CP2 actually contains for this.
    public void testShouldNotCrash() {
        final DeviceLocalContactsFilterProvider sut = new DeviceLocalContactsFilterProvider(
                getContext(), DeviceAccountFilter.ONLY_NULL);
        final CursorLoader loader = sut.onCreateLoader(0, null);
        getLoaderResultSynchronously(loader);
        // We didn't throw so it passed
    }

    public void testCreatesNoFiltersIfNoRawContactsHaveDeviceAccountType() {
        final DeviceLocalContactsFilterProvider sut = createWithFilterAndLoaderResult(
                DeviceAccountFilter.ONLY_NULL, queryResult(
                        "user", "com.example",
                        "user", "com.example",
                        "user", "com.example"));
        sut.setKnownAccountTypes("com.example");

        doLoad(sut);

        assertEquals(0, sut.getListFilters().size());
    }

    public void testCreatesOneFilterForDeviceAccount() {
        final DeviceLocalContactsFilterProvider sut = createWithFilterAndLoaderResult(
                DeviceAccountFilter.ONLY_NULL, queryResult(
                        "user", "com.example",
                        "user", "com.example",
                        null, null,
                        "user", "com.example",
                        null, null));
        sut.setKnownAccountTypes("com.example");

        doLoad(sut);

        assertEquals(1, sut.getListFilters().size());
        assertEquals(ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS,
                sut.getListFilters().get(0).filterType);
    }

    public void testCreatesOneFilterForEachDeviceAccount() {
         final DeviceLocalContactsFilterProvider sut = createWithFilterAndLoaderResult(
                 filterAllowing(null, "vnd.sec.contact.phone", "vnd.sec.contact.sim"), queryResult(
                         "sim_account", "vnd.sec.contact.sim",
                         "user", "com.example",
                         "user", "com.example",
                         "phone_account", "vnd.sec.contact.phone",
                         null, null,
                         "phone_account", "vnd.sec.contact.phone",
                         "user", "com.example",
                         null, null,
                         "sim_account", "vnd.sec.contact.sim",
                         "sim_account_2", "vnd.sec.contact.sim"
                 ));
        sut.setKnownAccountTypes("com.example");

        doLoad(sut);

        assertEquals(4, sut.getListFilters().size());
    }

    public void testFilterIsUpdatedWhenLoaderReloads() {
        final FakeContactsProvider provider = new FakeContactsProvider();
        final DeviceLocalContactsFilterProvider sut = new DeviceLocalContactsFilterProvider(
                createStubContextWithContactsProvider(provider), DeviceAccountFilter.ONLY_NULL);
        sut.setKnownAccountTypes("com.example");

        provider.setNextQueryResult(queryResult(
                null, null,
                "user", "com.example",
                "user", "com.example"
        ));
        doLoad(sut);

        assertFalse(sut.getListFilters().isEmpty());

        provider.setNextQueryResult(queryResult(
                "user", "com.example",
                "user", "com.example"
        ));
        doLoad(sut);

        assertTrue(sut.getListFilters().isEmpty());
    }

    public void testDoesNotCreateFiltersForKnownAccounts() {
        final DeviceLocalContactsFilterProvider sut = new DeviceLocalContactsFilterProvider(
                getContext(), DeviceAccountFilter.ONLY_NULL);
        sut.setKnownAccountTypes("com.example", "maybe_syncable_device_account_type");

        final CursorLoader loader = sut.onCreateLoader(0, null);

        // The filtering is done at the DB level rather than in the code so just verify that
        // selection is about right.
        assertTrue("Loader selection is wrong", loader.getSelection().contains("NOT IN (?,?)"));
        assertEquals("com.example", loader.getSelectionArgs()[0]);
        assertEquals("maybe_syncable_device_account_type", loader.getSelectionArgs()[1]);
    }

    private void doLoad(DeviceLocalContactsFilterProvider loaderCallbacks) {
        final CursorLoader loader = loaderCallbacks.onCreateLoader(0, null);
        final Cursor cursor = getLoaderResultSynchronously(loader);
        loaderCallbacks.onLoadFinished(loader, cursor);
    }

    private DeviceLocalContactsFilterProvider createWithFilterAndLoaderResult(
            DeviceAccountFilter filter, Cursor cursor) {
        final DeviceLocalContactsFilterProvider result = new DeviceLocalContactsFilterProvider(
                createStubContextWithContentQueryResult(cursor), filter);
        return result;
    }

    private Context createStubContextWithContentQueryResult(final Cursor cursor) {
        return createStubContextWithContactsProvider(new FakeContactsProvider(cursor));
    }

    private Context createStubContextWithContactsProvider(ContentProvider contactsProvider) {
        final MockContentResolver resolver = new MockContentResolver();
        resolver.addProvider(ContactsContract.AUTHORITY, contactsProvider);

        final Context context = Mockito.mock(MockContext.class);
        when(context.getContentResolver()).thenReturn(resolver);

        // The loader pulls out the application context instead of usign the context directly
        when(context.getApplicationContext()).thenReturn(context);

        return context;
    }

    private Cursor queryResult(String... typeNamePairs) {
        final MatrixCursor cursor = new MatrixCursor(new String[]
                { RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE });
        for (int i = 0; i < typeNamePairs.length; i += 2) {
            cursor.newRow().add(typeNamePairs[i]).add(typeNamePairs[i+1]);
        }
        return cursor;
    }

    private DeviceAccountFilter filterAllowing(String... accountTypes) {
        final Set<String> allowed = new HashSet<>(Arrays.asList(accountTypes));
        return new DeviceAccountFilter() {
            @Override
            public boolean isDeviceAccountType(String accountType) {
                return allowed.contains(accountType);
            }
        };
    }

    private static class FakeContactsProvider extends MockContentProvider {
        public Cursor mNextQueryResult;

        public FakeContactsProvider() {}

        public FakeContactsProvider(Cursor result) {
            mNextQueryResult = result;
        }

        public void setNextQueryResult(Cursor result) {
            mNextQueryResult = result;
        }

        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                String sortOrder) {
            return query(uri, projection, selection, selectionArgs, sortOrder, null);
        }

        @Nullable
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                String sortOrder, CancellationSignal cancellationSignal) {
            return mNextQueryResult;
        }
    }
}