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

Commit 66c2f93e authored by Hyundo Moon's avatar Hyundo Moon Committed by Gerrit Code Review
Browse files

Merge "Add BluetoothPbapCallLogComposerTest"

parents 0eae4a48 2028507b
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);
    }
}