Loading android/app/src/com/android/bluetooth/AlertActivity.java +8 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ import android.os.Bundle; import android.view.ViewGroup; import android.view.Window; import android.view.accessibility.AccessibilityEvent; import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; /** * An activity that follows the visual style of an AlertDialog. Loading Loading @@ -119,6 +122,11 @@ public abstract class AlertActivity extends Activity implements DialogInterface. mAlert.getButton(identifier).setEnabled(enable); } @VisibleForTesting public Button getButton(int identifier) { return mAlert.getButton(identifier); } @Override protected void onDestroy() { if (mAlert != null) { Loading android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java +12 −5 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.widget.EditText; import android.widget.TextView; import com.android.bluetooth.R; import com.android.internal.annotations.VisibleForTesting; /** * PbapActivity shows two dialogues: One for accepting incoming pbap request and Loading @@ -68,7 +69,8 @@ public class BluetoothPbapActivity extends AlertActivity private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16; private static final int DIALOG_YES_NO_AUTH = 1; @VisibleForTesting static final int DIALOG_YES_NO_AUTH = 1; private static final String KEY_USER_TIMEOUT = "user_timeout"; Loading @@ -80,7 +82,8 @@ public class BluetoothPbapActivity extends AlertActivity private String mSessionKey = ""; private int mCurrentDialog; @VisibleForTesting int mCurrentDialog; private boolean mTimeout = false; Loading @@ -90,7 +93,8 @@ public class BluetoothPbapActivity extends AlertActivity private BluetoothDevice mDevice; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @VisibleForTesting BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) { Loading Loading @@ -164,7 +168,8 @@ public class BluetoothPbapActivity extends AlertActivity } } private void onPositive() { @VisibleForTesting void onPositive() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { mSessionKey = mKeyView.getText().toString(); } Loading @@ -180,7 +185,8 @@ public class BluetoothPbapActivity extends AlertActivity finish(); } private void onNegative() { @VisibleForTesting void onNegative() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null); mKeyView.removeTextChangedListener(this); Loading @@ -199,6 +205,7 @@ public class BluetoothPbapActivity extends AlertActivity sendBroadcast(intent); } @VisibleForTesting private void onTimeout() { mTimeout = true; if (mCurrentDialog == DIALOG_YES_NO_AUTH) { Loading android/app/src/com/android/bluetooth/sap/SapMessage.java +6 −16 Original line number Diff line number Diff line Loading @@ -6,6 +6,8 @@ import android.hardware.radio.V1_0.SapTransferProtocol; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.google.protobuf.micro.CodedOutputStreamMicro; import com.google.protobuf.micro.InvalidProtocolBufferMicroException; Loading Loading @@ -198,7 +200,8 @@ public class SapMessage { this.mMsgType = msgType; } private static void resetPendingRilMessages() { @VisibleForTesting static void resetPendingRilMessages() { int numMessages = sOngoingRequests.size(); if (numMessages != 0) { Log.w(TAG, "Clearing message queue with size: " + numMessages); Loading Loading @@ -330,7 +333,8 @@ public class SapMessage { this.mTestMode = testMode; } private int getParamCount() { @VisibleForTesting int getParamCount() { int paramCount = 0; if (mMaxMsgSize != INVALID_VALUE) { paramCount++; Loading Loading @@ -725,20 +729,6 @@ public class SapMessage { * RILD Interface message conversion functions. ***************************************************************************/ /** * We use this function to * @param length * @param rawOut * @throws IOException */ private void writeLength(int length, CodedOutputStreamMicro out) throws IOException { byte[] dataLength = new byte[4]; dataLength[0] = dataLength[1] = 0; dataLength[2] = (byte) ((length >> 8) & 0xff); dataLength[3] = (byte) ((length) & 0xff); out.writeRawBytes(dataLength); } private ArrayList<Byte> primitiveArrayToContainerArrayList(byte[] arr) { ArrayList<Byte> arrayList = new ArrayList<>(arr.length); for (byte b : arr) { Loading android/app/tests/unit/src/com/android/bluetooth/mapapi/BluetoothMapIMProviderTest.java +144 −0 Original line number Diff line number Diff line Loading @@ -22,7 +22,10 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.doReturn; import android.content.ContentResolver; import android.content.ContentValues; Loading @@ -31,6 +34,11 @@ import android.content.pm.ProviderInfo; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.content.ContextWrapper; import org.mockito.Mockito; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; Loading @@ -42,10 +50,17 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.time.Instant; import java.util.Set; import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.AbstractMap; @RunWith(AndroidJUnit4.class) public class BluetoothMapIMProviderTest { private static final String TAG = "MapIMProviderTest"; private static final String AUTHORITY = "com.test"; private static final String ACCOUNT_ID = "12345"; private static final String MESSAGE_ID = "987654321"; Loading Loading @@ -452,6 +467,135 @@ public class BluetoothMapIMProviderTest { assertThat(BluetoothMapEmailProvider.getAccountId(messageUri)).isEqualTo(ACCOUNT_ID); } @Test public void onAccountChanged() { ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = AUTHORITY; providerInfo.exported = true; providerInfo.writePermission = android.Manifest.permission.BLUETOOTH_MAP; ContentResolver resolver = mock(ContentResolver.class); Context context = spy(new ContextWrapper(mContext)); doReturn(resolver).when(context).getContentResolver(); mProvider.attachInfo(context, providerInfo); Uri expectedUri; expectedUri = BluetoothMapContract.buildAccountUri(AUTHORITY); mProvider.onAccountChanged(null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String accountId = "32608910"; expectedUri = BluetoothMapContract.buildAccountUriwithId(AUTHORITY, accountId); mProvider.onAccountChanged(accountId); verify(resolver).notifyChange(expectedUri, null); } @Test public void onContactChanged() { ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = AUTHORITY; providerInfo.exported = true; providerInfo.writePermission = android.Manifest.permission.BLUETOOTH_MAP; ContentResolver resolver = mock(ContentResolver.class); Context context = spy(new ContextWrapper(mContext)); doReturn(resolver).when(context).getContentResolver(); mProvider.attachInfo(context, providerInfo); Uri expectedUri; expectedUri = BluetoothMapContract.buildConvoContactsUri(AUTHORITY); mProvider.onContactChanged(null,null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String accountId = "32608910"; expectedUri = BluetoothMapContract.buildConvoContactsUri(AUTHORITY, accountId); mProvider.onContactChanged(accountId, null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String contactId = "23623"; expectedUri = BluetoothMapContract.buildConvoContactsUriWithId( AUTHORITY, accountId, contactId); mProvider.onContactChanged(accountId, contactId); verify(resolver).notifyChange(expectedUri, null); } @Test public void onMessageChanged() { ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = AUTHORITY; providerInfo.exported = true; providerInfo.writePermission = android.Manifest.permission.BLUETOOTH_MAP; ContentResolver resolver = mock(ContentResolver.class); Context context = spy(new ContextWrapper(mContext)); doReturn(resolver).when(context).getContentResolver(); mProvider.attachInfo(context, providerInfo); Uri expectedUri; expectedUri = BluetoothMapContract.buildMessageUri(AUTHORITY); mProvider.onMessageChanged(null, null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String accountId = "32608910"; expectedUri = BluetoothMapContract.buildMessageUri(AUTHORITY, accountId); mProvider.onMessageChanged(accountId, null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String messageId = "23623"; expectedUri = BluetoothMapContract.buildMessageUriWithId( AUTHORITY, accountId, messageId); mProvider.onMessageChanged(accountId, messageId); verify(resolver).notifyChange(expectedUri, null); } @Test public void createContentValues_throwsIAE_forUnknownDataType() { Set<Map.Entry<String, Object>> valueSet = new HashSet<>(); Map<String, String> keyMap = new HashMap<>(); String key = "test_key"; Uri unknownTypeObject = Uri.parse("http://www.google.com"); valueSet.add(new AbstractMap.SimpleEntry<String, Object>(key, unknownTypeObject)); try { mProvider.createContentValues(valueSet, keyMap); assertWithMessage("IllegalArgumentException should be thrown.").fail(); } catch (IllegalArgumentException ex) { // Expected } } @Test public void createContentValues_success() { Map<String, String> keyMap = new HashMap<>(); String key = "test_key"; String convertedKey = "test_converted_key"; keyMap.put(key, convertedKey); Object[] valuesToTest = new Object[] { null, true, (byte) 0x01, new byte[] {0x01, 0x02}, 0.01, 0.01f, 123, 12345L, (short) 10, "testString" }; for (Object value : valuesToTest) { Log.d(TAG, "value=" + value); Set<Map.Entry<String, Object>> valueSet = new HashSet<>(); valueSet.add(new AbstractMap.SimpleEntry<String, Object>(key, value)); ContentValues contentValues = mProvider.createContentValues(valueSet, keyMap); assertThat(contentValues.get(convertedKey)).isEqualTo(value); } } public static class TestBluetoothMapIMProvider extends BluetoothMapIMProvider { @Override public boolean onCreate() { Loading android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java 0 → 100644 +173 −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 android.content.DialogInterface.BUTTON_POSITIVE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.DONT_KILL_APP; import static androidx.lifecycle.Lifecycle.State; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.text.Editable; import android.text.SpannableStringBuilder; import androidx.test.core.app.ActivityScenario; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; @LargeTest @RunWith(AndroidJUnit4.class) public class BluetoothPbapActivityTest { Context mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Intent mIntent; ActivityScenario<BluetoothPbapActivity> mActivityScenario; @Before public void setUp() { mIntent = new Intent(); mIntent.setClass(mTargetContext, BluetoothPbapActivity.class); mIntent.setAction(BluetoothPbapService.AUTH_CHALL_ACTION); enableActivity(true); mActivityScenario = ActivityScenario.launch(mIntent); } @After public void tearDown() throws Exception { if (mActivityScenario != null) { // Workaround for b/159805732. Without this, test hangs for 45 seconds. Thread.sleep(1_000); mActivityScenario.close(); } enableActivity(false); } @Test public void activityIsDestroyed_whenLaunchedWithoutIntentAction() throws Exception { mActivityScenario.close(); mIntent.setAction(null); mActivityScenario = ActivityScenario.launch(mIntent); assertActivityState(DESTROYED); } @Test public void onPreferenceChange_returnsTrue() throws Exception { AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> result.set( activity.onPreferenceChange(/*preference=*/null, /*newValue=*/null))); assertThat(result.get()).isTrue(); } @Test public void onPositive_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onPositive(); }); assertActivityState(DESTROYED); } @Test public void onNegative_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onNegative(); }); assertActivityState(DESTROYED); } @Test public void onReceiveTimeoutIntent_finishesActivity() throws Exception { Intent intent = new Intent(BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION); mActivityScenario.onActivity(activity -> { activity.mReceiver.onReceive(activity, intent); }); assertActivityState(DESTROYED); } @Test public void afterTextChanged() throws Exception { Editable editable = new SpannableStringBuilder("An editable text"); AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> { activity.afterTextChanged(editable); result.set(activity.getButton(BUTTON_POSITIVE).isEnabled()); }); assertThat(result.get()).isTrue(); } // TODO: Test onSaveInstanceState and onRestoreInstanceState. // Note: Activity.recreate() fails. The Activity just finishes itself when recreated. // Fix the bug and test those methods. @Test public void emptyMethods_doesNotThrowException() throws Exception { try { mActivityScenario.onActivity(activity -> { activity.beforeTextChanged(null, 0, 0, 0); activity.onTextChanged(null, 0, 0, 0); }); } catch (Exception ex) { assertWithMessage("Exception should not happen!").fail(); } } private void assertActivityState(State state) throws Exception { // TODO: Change this into an event driven systems Thread.sleep(3_000); assertThat(mActivityScenario.getState()).isEqualTo(state); } private void enableActivity(boolean enable) { int enabledState = enable ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DEFAULT; mTargetContext.getPackageManager().setApplicationEnabledSetting( mTargetContext.getPackageName(), enabledState, DONT_KILL_APP); ComponentName activityName = new ComponentName(mTargetContext, BluetoothPbapActivity.class); mTargetContext.getPackageManager().setComponentEnabledSetting( activityName, enabledState, DONT_KILL_APP); } } Loading
android/app/src/com/android/bluetooth/AlertActivity.java +8 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ import android.os.Bundle; import android.view.ViewGroup; import android.view.Window; import android.view.accessibility.AccessibilityEvent; import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; /** * An activity that follows the visual style of an AlertDialog. Loading Loading @@ -119,6 +122,11 @@ public abstract class AlertActivity extends Activity implements DialogInterface. mAlert.getButton(identifier).setEnabled(enable); } @VisibleForTesting public Button getButton(int identifier) { return mAlert.getButton(identifier); } @Override protected void onDestroy() { if (mAlert != null) { Loading
android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java +12 −5 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.widget.EditText; import android.widget.TextView; import com.android.bluetooth.R; import com.android.internal.annotations.VisibleForTesting; /** * PbapActivity shows two dialogues: One for accepting incoming pbap request and Loading @@ -68,7 +69,8 @@ public class BluetoothPbapActivity extends AlertActivity private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16; private static final int DIALOG_YES_NO_AUTH = 1; @VisibleForTesting static final int DIALOG_YES_NO_AUTH = 1; private static final String KEY_USER_TIMEOUT = "user_timeout"; Loading @@ -80,7 +82,8 @@ public class BluetoothPbapActivity extends AlertActivity private String mSessionKey = ""; private int mCurrentDialog; @VisibleForTesting int mCurrentDialog; private boolean mTimeout = false; Loading @@ -90,7 +93,8 @@ public class BluetoothPbapActivity extends AlertActivity private BluetoothDevice mDevice; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @VisibleForTesting BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) { Loading Loading @@ -164,7 +168,8 @@ public class BluetoothPbapActivity extends AlertActivity } } private void onPositive() { @VisibleForTesting void onPositive() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { mSessionKey = mKeyView.getText().toString(); } Loading @@ -180,7 +185,8 @@ public class BluetoothPbapActivity extends AlertActivity finish(); } private void onNegative() { @VisibleForTesting void onNegative() { if (mCurrentDialog == DIALOG_YES_NO_AUTH) { sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null); mKeyView.removeTextChangedListener(this); Loading @@ -199,6 +205,7 @@ public class BluetoothPbapActivity extends AlertActivity sendBroadcast(intent); } @VisibleForTesting private void onTimeout() { mTimeout = true; if (mCurrentDialog == DIALOG_YES_NO_AUTH) { Loading
android/app/src/com/android/bluetooth/sap/SapMessage.java +6 −16 Original line number Diff line number Diff line Loading @@ -6,6 +6,8 @@ import android.hardware.radio.V1_0.SapTransferProtocol; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.google.protobuf.micro.CodedOutputStreamMicro; import com.google.protobuf.micro.InvalidProtocolBufferMicroException; Loading Loading @@ -198,7 +200,8 @@ public class SapMessage { this.mMsgType = msgType; } private static void resetPendingRilMessages() { @VisibleForTesting static void resetPendingRilMessages() { int numMessages = sOngoingRequests.size(); if (numMessages != 0) { Log.w(TAG, "Clearing message queue with size: " + numMessages); Loading Loading @@ -330,7 +333,8 @@ public class SapMessage { this.mTestMode = testMode; } private int getParamCount() { @VisibleForTesting int getParamCount() { int paramCount = 0; if (mMaxMsgSize != INVALID_VALUE) { paramCount++; Loading Loading @@ -725,20 +729,6 @@ public class SapMessage { * RILD Interface message conversion functions. ***************************************************************************/ /** * We use this function to * @param length * @param rawOut * @throws IOException */ private void writeLength(int length, CodedOutputStreamMicro out) throws IOException { byte[] dataLength = new byte[4]; dataLength[0] = dataLength[1] = 0; dataLength[2] = (byte) ((length >> 8) & 0xff); dataLength[3] = (byte) ((length) & 0xff); out.writeRawBytes(dataLength); } private ArrayList<Byte> primitiveArrayToContainerArrayList(byte[] arr) { ArrayList<Byte> arrayList = new ArrayList<>(arr.length); for (byte b : arr) { Loading
android/app/tests/unit/src/com/android/bluetooth/mapapi/BluetoothMapIMProviderTest.java +144 −0 Original line number Diff line number Diff line Loading @@ -22,7 +22,10 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.doReturn; import android.content.ContentResolver; import android.content.ContentValues; Loading @@ -31,6 +34,11 @@ import android.content.pm.ProviderInfo; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.content.ContextWrapper; import org.mockito.Mockito; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; Loading @@ -42,10 +50,17 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.time.Instant; import java.util.Set; import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.AbstractMap; @RunWith(AndroidJUnit4.class) public class BluetoothMapIMProviderTest { private static final String TAG = "MapIMProviderTest"; private static final String AUTHORITY = "com.test"; private static final String ACCOUNT_ID = "12345"; private static final String MESSAGE_ID = "987654321"; Loading Loading @@ -452,6 +467,135 @@ public class BluetoothMapIMProviderTest { assertThat(BluetoothMapEmailProvider.getAccountId(messageUri)).isEqualTo(ACCOUNT_ID); } @Test public void onAccountChanged() { ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = AUTHORITY; providerInfo.exported = true; providerInfo.writePermission = android.Manifest.permission.BLUETOOTH_MAP; ContentResolver resolver = mock(ContentResolver.class); Context context = spy(new ContextWrapper(mContext)); doReturn(resolver).when(context).getContentResolver(); mProvider.attachInfo(context, providerInfo); Uri expectedUri; expectedUri = BluetoothMapContract.buildAccountUri(AUTHORITY); mProvider.onAccountChanged(null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String accountId = "32608910"; expectedUri = BluetoothMapContract.buildAccountUriwithId(AUTHORITY, accountId); mProvider.onAccountChanged(accountId); verify(resolver).notifyChange(expectedUri, null); } @Test public void onContactChanged() { ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = AUTHORITY; providerInfo.exported = true; providerInfo.writePermission = android.Manifest.permission.BLUETOOTH_MAP; ContentResolver resolver = mock(ContentResolver.class); Context context = spy(new ContextWrapper(mContext)); doReturn(resolver).when(context).getContentResolver(); mProvider.attachInfo(context, providerInfo); Uri expectedUri; expectedUri = BluetoothMapContract.buildConvoContactsUri(AUTHORITY); mProvider.onContactChanged(null,null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String accountId = "32608910"; expectedUri = BluetoothMapContract.buildConvoContactsUri(AUTHORITY, accountId); mProvider.onContactChanged(accountId, null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String contactId = "23623"; expectedUri = BluetoothMapContract.buildConvoContactsUriWithId( AUTHORITY, accountId, contactId); mProvider.onContactChanged(accountId, contactId); verify(resolver).notifyChange(expectedUri, null); } @Test public void onMessageChanged() { ProviderInfo providerInfo = new ProviderInfo(); providerInfo.authority = AUTHORITY; providerInfo.exported = true; providerInfo.writePermission = android.Manifest.permission.BLUETOOTH_MAP; ContentResolver resolver = mock(ContentResolver.class); Context context = spy(new ContextWrapper(mContext)); doReturn(resolver).when(context).getContentResolver(); mProvider.attachInfo(context, providerInfo); Uri expectedUri; expectedUri = BluetoothMapContract.buildMessageUri(AUTHORITY); mProvider.onMessageChanged(null, null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String accountId = "32608910"; expectedUri = BluetoothMapContract.buildMessageUri(AUTHORITY, accountId); mProvider.onMessageChanged(accountId, null); verify(resolver).notifyChange(expectedUri, null); Mockito.clearInvocations(resolver); String messageId = "23623"; expectedUri = BluetoothMapContract.buildMessageUriWithId( AUTHORITY, accountId, messageId); mProvider.onMessageChanged(accountId, messageId); verify(resolver).notifyChange(expectedUri, null); } @Test public void createContentValues_throwsIAE_forUnknownDataType() { Set<Map.Entry<String, Object>> valueSet = new HashSet<>(); Map<String, String> keyMap = new HashMap<>(); String key = "test_key"; Uri unknownTypeObject = Uri.parse("http://www.google.com"); valueSet.add(new AbstractMap.SimpleEntry<String, Object>(key, unknownTypeObject)); try { mProvider.createContentValues(valueSet, keyMap); assertWithMessage("IllegalArgumentException should be thrown.").fail(); } catch (IllegalArgumentException ex) { // Expected } } @Test public void createContentValues_success() { Map<String, String> keyMap = new HashMap<>(); String key = "test_key"; String convertedKey = "test_converted_key"; keyMap.put(key, convertedKey); Object[] valuesToTest = new Object[] { null, true, (byte) 0x01, new byte[] {0x01, 0x02}, 0.01, 0.01f, 123, 12345L, (short) 10, "testString" }; for (Object value : valuesToTest) { Log.d(TAG, "value=" + value); Set<Map.Entry<String, Object>> valueSet = new HashSet<>(); valueSet.add(new AbstractMap.SimpleEntry<String, Object>(key, value)); ContentValues contentValues = mProvider.createContentValues(valueSet, keyMap); assertThat(contentValues.get(convertedKey)).isEqualTo(value); } } public static class TestBluetoothMapIMProvider extends BluetoothMapIMProvider { @Override public boolean onCreate() { Loading
android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java 0 → 100644 +173 −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 android.content.DialogInterface.BUTTON_POSITIVE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.DONT_KILL_APP; import static androidx.lifecycle.Lifecycle.State; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.text.Editable; import android.text.SpannableStringBuilder; import androidx.test.core.app.ActivityScenario; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; @LargeTest @RunWith(AndroidJUnit4.class) public class BluetoothPbapActivityTest { Context mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Intent mIntent; ActivityScenario<BluetoothPbapActivity> mActivityScenario; @Before public void setUp() { mIntent = new Intent(); mIntent.setClass(mTargetContext, BluetoothPbapActivity.class); mIntent.setAction(BluetoothPbapService.AUTH_CHALL_ACTION); enableActivity(true); mActivityScenario = ActivityScenario.launch(mIntent); } @After public void tearDown() throws Exception { if (mActivityScenario != null) { // Workaround for b/159805732. Without this, test hangs for 45 seconds. Thread.sleep(1_000); mActivityScenario.close(); } enableActivity(false); } @Test public void activityIsDestroyed_whenLaunchedWithoutIntentAction() throws Exception { mActivityScenario.close(); mIntent.setAction(null); mActivityScenario = ActivityScenario.launch(mIntent); assertActivityState(DESTROYED); } @Test public void onPreferenceChange_returnsTrue() throws Exception { AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> result.set( activity.onPreferenceChange(/*preference=*/null, /*newValue=*/null))); assertThat(result.get()).isTrue(); } @Test public void onPositive_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onPositive(); }); assertActivityState(DESTROYED); } @Test public void onNegative_finishesActivity() throws Exception { mActivityScenario.onActivity(activity -> { activity.onNegative(); }); assertActivityState(DESTROYED); } @Test public void onReceiveTimeoutIntent_finishesActivity() throws Exception { Intent intent = new Intent(BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION); mActivityScenario.onActivity(activity -> { activity.mReceiver.onReceive(activity, intent); }); assertActivityState(DESTROYED); } @Test public void afterTextChanged() throws Exception { Editable editable = new SpannableStringBuilder("An editable text"); AtomicBoolean result = new AtomicBoolean(false); mActivityScenario.onActivity(activity -> { activity.afterTextChanged(editable); result.set(activity.getButton(BUTTON_POSITIVE).isEnabled()); }); assertThat(result.get()).isTrue(); } // TODO: Test onSaveInstanceState and onRestoreInstanceState. // Note: Activity.recreate() fails. The Activity just finishes itself when recreated. // Fix the bug and test those methods. @Test public void emptyMethods_doesNotThrowException() throws Exception { try { mActivityScenario.onActivity(activity -> { activity.beforeTextChanged(null, 0, 0, 0); activity.onTextChanged(null, 0, 0, 0); }); } catch (Exception ex) { assertWithMessage("Exception should not happen!").fail(); } } private void assertActivityState(State state) throws Exception { // TODO: Change this into an event driven systems Thread.sleep(3_000); assertThat(mActivityScenario.getState()).isEqualTo(state); } private void enableActivity(boolean enable) { int enabledState = enable ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DEFAULT; mTargetContext.getPackageManager().setApplicationEnabledSetting( mTargetContext.getPackageName(), enabledState, DONT_KILL_APP); ComponentName activityName = new ComponentName(mTargetContext, BluetoothPbapActivity.class); mTargetContext.getPackageManager().setComponentEnabledSetting( activityName, enabledState, DONT_KILL_APP); } }