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

Commit b1e2ab1d authored by Hyundo Moon's avatar Hyundo Moon Committed by Cherrypicker Worker
Browse files

Add BluetoothPbapCallLogComposerTest

Bug: 237548430
Test: atest BluetoothPbapCallLogComposerTest
Ignore-AOSP-First: This is a cherry-pick CL
Change-Id: Id3394263a2f9dc0fb452addaabe371cc6d5fd958
Merged-In: Id3394263a2f9dc0fb452addaabe371cc6d5fd958
(cherry picked from commit cef5c8fc)
Merged-In: Id3394263a2f9dc0fb452addaabe371cc6d5fd958
parent e87b7b0e
Loading
Loading
Loading
Loading
+16 −12
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */
package com.android.bluetooth.pbap;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -26,6 +25,7 @@ import android.text.TextUtils;
import android.util.Log;

import com.android.bluetooth.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.vcard.VCardBuilder;
import com.android.vcard.VCardConfig;
import com.android.vcard.VCardConstants;
@@ -41,19 +41,24 @@ import java.util.Calendar;
public class BluetoothPbapCallLogComposer {
    private static final String TAG = "PbapCallLogComposer";

    private static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
    @VisibleForTesting
    static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
            "Failed to get database information";

    private static final String FAILURE_REASON_NO_ENTRY = "There's no exportable in the database";
    @VisibleForTesting
    static final String FAILURE_REASON_NO_ENTRY = "There's no exportable in the database";

    private static final String FAILURE_REASON_NOT_INITIALIZED =
    @VisibleForTesting
    static final String FAILURE_REASON_NOT_INITIALIZED =
            "The vCard composer object is not correctly initialized";

    /** Should be visible only from developers... (no need to translate, hopefully) */
    private static final String FAILURE_REASON_UNSUPPORTED_URI =
    @VisibleForTesting
    static final String FAILURE_REASON_UNSUPPORTED_URI =
            "The Uri vCard composer received is not supported by the composer.";

    private static final String NO_ERROR = "No error";
    @VisibleForTesting
    static final String NO_ERROR = "No error";

    /** The projection to use when querying the call log table */
    private static final String[] sCallLogProjection = new String[]{
@@ -80,7 +85,6 @@ public class BluetoothPbapCallLogComposer {
    private static final String VCARD_PROPERTY_CALLTYPE_MISSED = "MISSED";

    private final Context mContext;
    private ContentResolver mContentResolver;
    private Cursor mCursor;

    private boolean mTerminateIsCalled;
@@ -91,7 +95,6 @@ public class BluetoothPbapCallLogComposer {

    public BluetoothPbapCallLogComposer(final Context context) {
        mContext = context;
        mContentResolver = context.getContentResolver();
    }

    public boolean init(final Uri contentUri, final String selection, final String[] selectionArgs,
@@ -104,8 +107,9 @@ public class BluetoothPbapCallLogComposer {
            return false;
        }

        mCursor =
                mContentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
        mCursor = BluetoothPbapMethodProxy.getInstance().contentResolverQuery(
                mContext.getContentResolver(), contentUri, projection, selection, selectionArgs,
                sortOrder);

        if (mCursor == null) {
            mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
@@ -175,8 +179,8 @@ public class BluetoothPbapCallLogComposer {
    /**
     * This static function is to compose vCard for phone own number
     */
    public String composeVCardForPhoneOwnNumber(int phonetype, String phoneName, String phoneNumber,
            boolean vcardVer21) {
    public static String composeVCardForPhoneOwnNumber(int phonetype, String phoneName,
            String phoneNumber, boolean vcardVer21) {
        final int vcardType = (vcardVer21 ? VCardConfig.VCARD_TYPE_V21_GENERIC
                : VCardConfig.VCARD_TYPE_V30_GENERIC)
                | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING;
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.bluetooth.pbap;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import com.android.bluetooth.Utils;
import com.android.internal.annotations.VisibleForTesting;

/**
 * Proxy class for method calls to help with unit testing
 */
public class BluetoothPbapMethodProxy {
    private static final String TAG = BluetoothPbapMethodProxy.class.getSimpleName();
    private static BluetoothPbapMethodProxy sInstance;
    private static final Object INSTANCE_LOCK = new Object();

    private BluetoothPbapMethodProxy() {}

    /**
     * Get the singleton instance of proxy
     *
     * @return the singleton instance, guaranteed not null
     */
    public static BluetoothPbapMethodProxy getInstance() {
        synchronized (INSTANCE_LOCK) {
            if (sInstance == null) {
                sInstance = new BluetoothPbapMethodProxy();
            }
        }
        return sInstance;
    }

    /**
     * Allow unit tests to substitute BluetoothPbapMethodCallProxy with a test instance
     *
     * @param proxy a test instance of the BluetoothPbapMethodCallProxy
     */
    @VisibleForTesting
    public static void setInstanceForTesting(BluetoothPbapMethodProxy proxy) {
        Utils.enforceInstrumentationTestMode();
        synchronized (INSTANCE_LOCK) {
            Log.d(TAG, "setInstanceForTesting(), set to " + proxy);
            sInstance = proxy;
        }
    }

    /**
     * Return the result of {@link ContentResolver#query(Uri, String[], String, String[], String)}.
     */
    public Cursor contentResolverQuery(ContentResolver contentResolver, final Uri contentUri,
            final String[] projection, final String selection, final String[] selectionArgs,
            final String sortOrder) {
        return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -143,8 +143,8 @@ public class BluetoothPbapVcardManager {
        BluetoothPbapCallLogComposer composer = new BluetoothPbapCallLogComposer(mContext);
        String name = BluetoothPbapService.getLocalPhoneName();
        String number = BluetoothPbapService.getLocalPhoneNum();
        String vcard = composer.composeVCardForPhoneOwnNumber(Phone.TYPE_MOBILE, name, number,
                vcardType21);
        String vcard = BluetoothPbapCallLogComposer.composeVCardForPhoneOwnNumber(
                Phone.TYPE_MOBILE, name, number, vcardType21);
        return vcard;
    }

+205 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.bluetooth.pbap;

import static com.android.bluetooth.pbap.BluetoothPbapCallLogComposer.FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
import static com.android.bluetooth.pbap.BluetoothPbapCallLogComposer.FAILURE_REASON_NOT_INITIALIZED;
import static com.android.bluetooth.pbap.BluetoothPbapCallLogComposer.FAILURE_REASON_NO_ENTRY;
import static com.android.bluetooth.pbap.BluetoothPbapCallLogComposer.FAILURE_REASON_UNSUPPORTED_URI;
import static com.android.bluetooth.pbap.BluetoothPbapCallLogComposer.NO_ERROR;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog;
import android.provider.ContactsContract;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class BluetoothPbapCallLogComposerTest {

    private static final Uri CALL_LOG_URI = CallLog.Calls.CONTENT_URI;

    // Note: These variables are used intentionally put as null,
    //       since the values are not at all used inside BluetoothPbapCallLogComposer.init().
    private static final String SELECTION = null;
    private static final String[] SELECTION_ARGS = null;
    private static final String SORT_ORDER = null;

    private BluetoothPbapCallLogComposer mComposer;

    @Spy
    BluetoothPbapMethodProxy mPbapCallProxy = BluetoothPbapMethodProxy.getInstance();

    @Mock
    Cursor mMockCursor;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        BluetoothPbapMethodProxy.setInstanceForTesting(mPbapCallProxy);

        doReturn(mMockCursor).when(mPbapCallProxy)
                .contentResolverQuery(any(), any(), any(), any(), any(), any());
        final int validRowCount = 5;
        when(mMockCursor.getCount()).thenReturn(validRowCount);
        when(mMockCursor.moveToFirst()).thenReturn(true);

        mComposer = new BluetoothPbapCallLogComposer(InstrumentationRegistry.getTargetContext());
    }

    @After
    public void tearDown() throws Exception {
        BluetoothPbapMethodProxy.setInstanceForTesting(null);
    }

    @Test
    public void testInit_success() {
        assertThat(mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER))
                .isTrue();
        assertThat(mComposer.getErrorReason()).isEqualTo(NO_ERROR);
    }

    @Test
    public void testInit_failWhenUriIsNotSupported() {
        final Uri uriOtherThanCallLog = Uri.parse("content://not/a/call/log/uri");
        assertThat(uriOtherThanCallLog).isNotEqualTo(CALL_LOG_URI);

        assertThat(mComposer.init(uriOtherThanCallLog, SELECTION, SELECTION_ARGS, SORT_ORDER))
                .isFalse();
        assertThat(mComposer.getErrorReason()).isEqualTo(FAILURE_REASON_UNSUPPORTED_URI);
    }

    @Test
    public void testInit_failWhenCursorIsNull() {
        doReturn(null).when(mPbapCallProxy)
                .contentResolverQuery(any(), any(), any(), any(), any(), any());

        assertThat(mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER))
                .isFalse();
        assertThat(mComposer.getErrorReason())
                .isEqualTo(FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO);
    }

    @Test
    public void testInit_failWhenCursorRowCountIsZero() {
        when(mMockCursor.getCount()).thenReturn(0);

        assertThat(mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER))
                .isFalse();
        assertThat(mComposer.getErrorReason()).isEqualTo(FAILURE_REASON_NO_ENTRY);
        verify(mMockCursor).close();
    }

    @Test
    public void testInit_failWhenCursorMoveToFirstFails() {
        when(mMockCursor.moveToFirst()).thenReturn(false);

        assertThat(mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER))
                .isFalse();
        assertThat(mComposer.getErrorReason()).isEqualTo(FAILURE_REASON_NO_ENTRY);
        verify(mMockCursor).close();
    }

    @Test
    public void testCreateOneEntry_success() {
        mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER);

        assertThat(mComposer.createOneEntry(true)).isNotEmpty();
        assertThat(mComposer.getErrorReason()).isEqualTo(NO_ERROR);
        verify(mMockCursor).moveToNext();
    }

    @Test
    public void testCreateOneEntry_failWhenNotInitialized() {
        assertThat(mComposer.createOneEntry(true)).isNull();
        assertThat(mComposer.getErrorReason()).isEqualTo(FAILURE_REASON_NOT_INITIALIZED);
    }

    @Test
    public void testComposeVCardForPhoneOwnNumber() {
        final int testPhoneType = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;
        final String testPhoneName = "test_phone_name";
        final String testPhoneNumber = "0123456789";

        assertThat(BluetoothPbapCallLogComposer.composeVCardForPhoneOwnNumber(
                testPhoneType, testPhoneName, testPhoneNumber, /*vcardVer21=*/ true))
                .contains(testPhoneNumber);
    }

    @Test
    public void testTerminate() {
        mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER);

        mComposer.terminate();
        verify(mMockCursor).close();
    }

    @Test
    public void testFinalize() {
        mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER);

        mComposer.finalize();
        verify(mMockCursor).close();
    }

    @Test
    public void testGetCount_success() {
        mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER);
        final int cursorRowCount = 15;
        when(mMockCursor.getCount()).thenReturn(cursorRowCount);

        assertThat(mComposer.getCount()).isEqualTo(cursorRowCount);
    }

    @Test
    public void testGetCount_returnsZeroWhenNotInitialized() {
        assertThat(mComposer.getCount()).isEqualTo(0);
    }

    @Test
    public void testIsAfterLast_success() {
        mComposer.init(CALL_LOG_URI, SELECTION, SELECTION_ARGS, SORT_ORDER);
        final boolean cursorIsAfterLast = true;
        when(mMockCursor.isAfterLast()).thenReturn(cursorIsAfterLast);

        assertThat(mComposer.isAfterLast()).isEqualTo(cursorIsAfterLast);
    }

    @Test
    public void testIsAfterLast_returnsFalseWhenNotInitialized() {
        assertThat(mComposer.isAfterLast()).isEqualTo(false);
    }
}