[Audiosharing] Increase test coverage.

Test: atest -c com.android.settings.connecteddevice.audiosharing.audiostream
Flag: com.android.settingslib.flags.enable_le_audio_qr_code_private_broadcast_sharing
Bug: 345686602
Change-Id: I668df463ec1c6e994d2dcaa363a20aa06f69f199
This commit is contained in:
chelseahao
2024-06-26 18:40:05 +08:00
committed by Chelsea Hao
parent 67d977b72e
commit 7529e1bea7
16 changed files with 933 additions and 27 deletions

View File

@@ -27,6 +27,7 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
@@ -48,7 +49,9 @@ public class AudioStreamButtonController extends BasePreferenceController
private static final String TAG = "AudioStreamButtonController";
private static final String KEY = "audio_stream_button";
private static final int SOURCE_ORIGIN_REPOSITORY = SourceOriginForLogging.REPOSITORY.ordinal();
private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
@VisibleForTesting
final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
new AudioStreamsBroadcastAssistantCallback() {
@Override
public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
@@ -97,8 +100,7 @@ public class AudioStreamButtonController extends BasePreferenceController
}
};
private final AudioStreamsRepository mAudioStreamsRepository =
AudioStreamsRepository.getInstance();
private AudioStreamsRepository mAudioStreamsRepository = AudioStreamsRepository.getInstance();
private final Executor mExecutor;
private final AudioStreamsHelper mAudioStreamsHelper;
private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
@@ -228,4 +230,9 @@ public class AudioStreamButtonController extends BasePreferenceController
void init(int broadcastId) {
mBroadcastId = broadcastId;
}
@VisibleForTesting
void setAudioStreamsRepositoryForTesting(AudioStreamsRepository repository) {
mAudioStreamsRepository = repository;
}
}

View File

@@ -31,6 +31,7 @@ import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
@@ -43,9 +44,12 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
private static final String TAG = "AudioStreamConfirmDialog";
private static final int DEFAULT_DEVICE_NAME = R.string.audio_streams_dialog_default_device;
@VisibleForTesting
static final int DEFAULT_DEVICE_NAME = R.string.audio_streams_dialog_default_device;
private Context mContext;
@Nullable private Activity mActivity;
@VisibleForTesting @Nullable Activity mActivity;
@Nullable private BluetoothLeBroadcastMetadata mBroadcastMetadata;
@Nullable private BluetoothDevice mConnectedDevice;
private int mAudioStreamConfirmDialogId = SettingsEnums.PAGE_UNKNOWN;

View File

@@ -54,7 +54,9 @@ public class AudioStreamHeaderController extends BasePreferenceController
private final Executor mExecutor;
private final AudioStreamsHelper mAudioStreamsHelper;
@Nullable private final LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
@VisibleForTesting
final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
new AudioStreamsBroadcastAssistantCallback() {
@Override
public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {

View File

@@ -23,7 +23,6 @@ import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -63,11 +62,6 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
return R.xml.bluetooth_le_audio_streams;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -91,11 +85,6 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

View File

@@ -16,22 +16,36 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.view.View;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.ActionButtonsPreference;
import org.junit.After;
@@ -39,14 +53,17 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -63,14 +80,23 @@ public class AudioStreamButtonControllerTest {
@Mock private AudioStreamsHelper mAudioStreamsHelper;
@Mock private PreferenceScreen mScreen;
@Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private AudioStreamsRepository mRepository;
@Mock private ActionButtonsPreference mPreference;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private FakeFeatureFactory mFeatureFactory;
private AudioStreamButtonController mController;
@Before
public void setUp() {
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mController = new AudioStreamButtonController(mContext, KEY);
mController.init(BROADCAST_ID);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
when(mPreference.getContext()).thenReturn(mContext);
when(mPreference.setButton1Text(anyInt())).thenReturn(mPreference);
@@ -85,6 +111,40 @@ public class AudioStreamButtonControllerTest {
ShadowAudioStreamsHelper.reset();
}
@Test
public void onStart_registerCallbacks() {
mController.onStart(mLifecycleOwner);
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStart_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamButtonController(mContext, KEY);
mController.onStart(mLifecycleOwner);
verify(mAssistant, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_unregisterCallbacks() {
mController.onStop(mLifecycleOwner);
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamButtonController(mContext, KEY);
mController.onStop(mLifecycleOwner);
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void testDisplayPreference_sourceConnected_setDisconnectButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
@@ -96,18 +156,133 @@ public class AudioStreamButtonControllerTest {
verify(mPreference).setButton1Enabled(true);
verify(mPreference).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference).setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
verify(mPreference).setButton1OnClickListener(any(View.OnClickListener.class));
ArgumentCaptor<View.OnClickListener> listenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
verify(mPreference).setButton1OnClickListener(listenerCaptor.capture());
var listener = listenerCaptor.getValue();
assertThat(listener).isNotNull();
listener.onClick(mock(View.class));
verify(mAudioStreamsHelper).removeSource(BROADCAST_ID);
verify(mPreference).setButton1Enabled(false);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_LEAVE_BUTTON_CLICK));
}
@Test
public void testDisplayPreference_sourceNotConnected_setConnectButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.setAudioStreamsRepositoryForTesting(mRepository);
var metadataToRejoin = mock(BluetoothLeBroadcastMetadata.class);
when(mRepository.getSavedMetadata(any(), anyInt())).thenReturn(metadataToRejoin);
mController.displayPreference(mScreen);
verify(mPreference).setButton1Enabled(true);
verify(mPreference).setButton1Text(R.string.audio_streams_connect);
verify(mPreference).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
verify(mPreference).setButton1OnClickListener(any(View.OnClickListener.class));
ArgumentCaptor<View.OnClickListener> listenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
verify(mPreference).setButton1OnClickListener(listenerCaptor.capture());
var listener = listenerCaptor.getValue();
assertThat(listener).isNotNull();
listener.onClick(mock(View.class));
verify(mAudioStreamsHelper).addSource(metadataToRejoin);
verify(mPreference).setButton1Enabled(false);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN), anyInt());
}
@Test
public void testCallback_onSourceRemoved_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceRemoved(
mock(BluetoothDevice.class), /* sourceId= */ 0, /* reason= */ 0);
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_connect);
verify(mPreference, times(2)).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
}
@Test
public void testCallback_onSourceRemovedFailed_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
mock(BluetoothDevice.class), /* sourceId= */ 0, /* reason= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_LEAVE_FAILED));
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference, times(2))
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
}
@Test
public void testCallback_onReceiveStateChanged_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(state.getBisSyncState()).thenReturn(bisSyncState);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mock(BluetoothDevice.class), /* sourceId= */ 0, state);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED), anyInt());
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference, times(2))
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
}
@Test
public void testCallback_onSourceAddFailed_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceAddFailed(
mock(BluetoothDevice.class),
mock(BluetoothLeBroadcastMetadata.class),
/* reason= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER), anyInt());
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_connect);
verify(mPreference, times(2)).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
}
@Test
public void testCallback_onSourceLost_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 0);
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_connect);
verify(mPreference, times(2)).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
}
}

View File

@@ -18,28 +18,132 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.settingslib.flags.Flags;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
})
public class AudioStreamConfirmDialogActivityTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private LocalBluetoothManager mLocalBluetoothManager;
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private VolumeControlProfile mVolumeControl;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private AudioStreamConfirmDialogActivity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.buildActivity(AudioStreamConfirmDialogActivity.class).get();
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile())
.thenReturn(mAssistant);
when(mLocalBluetoothProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
when(mBroadcast.isProfileReady()).thenReturn(true);
when(mAssistant.isProfileReady()).thenReturn(true);
when(mVolumeControl.isProfileReady()).thenReturn(true);
}
@After
public void tearDown() {
ShadowBluetoothUtils.reset();
}
@Test
public void isValidFragment_returnsTrue() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
assertThat(mActivity.isValidFragment(AudioStreamConfirmDialog.class.getName())).isTrue();
}
@Test
public void isValidFragment_returnsFalse() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
assertThat(mActivity.isValidFragment("")).isFalse();
}
@Test
public void isToolbarEnabled_returnsFalse() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
assertThat(mActivity.isToolbarEnabled()).isFalse();
}
@Test
public void setupActivity_serviceNotReady_registerCallback() {
when(mBroadcast.isProfileReady()).thenReturn(false);
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
verify(mLocalBluetoothProfileManager).addServiceListener(any());
}
@Test
public void setupActivity_serviceNotReady_registerCallback_onServiceCallback() {
when(mBroadcast.isProfileReady()).thenReturn(false);
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
verify(mLocalBluetoothProfileManager).addServiceListener(any());
when(mBroadcast.isProfileReady()).thenReturn(true);
mActivity.onServiceConnected();
verify(mLocalBluetoothProfileManager).removeServiceListener(any());
mActivity.onServiceDisconnected();
// Do nothing.
}
@Test
public void setupActivity_serviceReady_doNothing() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
verify(mLocalBluetoothProfileManager, never()).addServiceListener(any());
}
@Test
public void onStop_unregisterCallback() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
mActivity.onStop();
verify(mLocalBluetoothProfileManager).removeServiceListener(any());
}
}

View File

@@ -16,14 +16,21 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static android.app.settings.SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamConfirmDialog.DEFAULT_DEVICE_NAME;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment.KEY_BROADCAST_METADATA;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -127,6 +134,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -152,6 +161,10 @@ public class AudioStreamConfirmDialogTest {
assertThat(rightButton).isNotNull();
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity).finish();
}
@Test
@@ -165,6 +178,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -184,11 +199,20 @@ public class AudioStreamConfirmDialogTest {
View leftButton = dialog.findViewById(R.id.left_button);
assertThat(leftButton).isNotNull();
assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(leftButton.hasOnClickListeners()).isTrue();
leftButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
Button rightButton = dialog.findViewById(R.id.right_button);
assertThat(rightButton).isNotNull();
assertThat(rightButton.getText())
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_button));
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity, times(2)).finish();
}
@Test
@@ -207,6 +231,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -231,6 +257,10 @@ public class AudioStreamConfirmDialogTest {
assertThat(rightButton).isNotNull();
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity).finish();
}
@Test
@@ -252,6 +282,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -276,6 +308,10 @@ public class AudioStreamConfirmDialogTest {
assertThat(rightButton).isNotNull();
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity).finish();
}
@Test
@@ -283,7 +319,7 @@ public class AudioStreamConfirmDialogTest {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mBluetoothDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
when(mBluetoothDevice.getAlias()).thenReturn("");
Intent intent = new Intent();
intent.putExtra(KEY_BROADCAST_METADATA, VALID_METADATA);
@@ -296,9 +332,11 @@ public class AudioStreamConfirmDialogTest {
shadowMainLooper().idle();
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN);
.isEqualTo(DIALOG_AUDIO_STREAM_CONFIRM_LISTEN);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
Dialog dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
TextView title = dialog.findViewById(R.id.dialog_title);
@@ -311,17 +349,27 @@ public class AudioStreamConfirmDialogTest {
assertThat(subtitle1.getVisibility()).isEqualTo(View.VISIBLE);
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
assertThat(subtitle2).isNotNull();
var defaultName = mContext.getString(DEFAULT_DEVICE_NAME);
assertThat(subtitle2.getText())
.isEqualTo(
mContext.getString(
R.string.audio_streams_dialog_control_volume, DEVICE_NAME));
R.string.audio_streams_dialog_control_volume, defaultName));
View leftButton = dialog.findViewById(R.id.left_button);
assertThat(leftButton).isNotNull();
assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(leftButton.hasOnClickListeners()).isTrue();
leftButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
Button rightButton = dialog.findViewById(R.id.right_button);
assertThat(rightButton).isNotNull();
assertThat(rightButton.getText())
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_listen));
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity, times(2)).finish();
}
}

View File

@@ -16,22 +16,48 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamDetailsFragment.BROADCAST_ID_ARG;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamDetailsFragment.BROADCAST_NAME_ARG;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioStreamDetailsFragmentTest {
private AudioStreamDetailsFragment mFragment;
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
private static final String BROADCAST_NAME = "name";
private static final int BROADCAST_ID = 1;
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock private AudioStreamHeaderController mHeaderController;
@Mock private AudioStreamButtonController mButtonController;
private TestFragment mFragment;
@Before
public void setUp() {
mFragment = new AudioStreamDetailsFragment();
mFragment = spy(new TestFragment());
doReturn(mHeaderController).when(mFragment).use(AudioStreamHeaderController.class);
doReturn(mButtonController).when(mFragment).use(AudioStreamButtonController.class);
}
@Test
@@ -44,4 +70,29 @@ public class AudioStreamDetailsFragmentTest {
public void getLogTag_returnsCorrectTag() {
assertThat(mFragment.getLogTag()).isEqualTo(AudioStreamDetailsFragment.TAG);
}
@Test
public void getMetricsCategory_returnsCorrectEnum() {
assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.AUDIO_STREAM_DETAIL);
}
@Test
public void onAttach_getArguments() {
Bundle bundle = new Bundle();
bundle.putString(BROADCAST_NAME_ARG, BROADCAST_NAME);
bundle.putInt(BROADCAST_ID_ARG, BROADCAST_ID);
mFragment.setArguments(bundle);
mFragment.onAttach(mContext);
verify(mButtonController).init(BROADCAST_ID);
verify(mHeaderController).init(mFragment, BROADCAST_NAME, BROADCAST_ID);
}
public static class TestFragment extends AudioStreamDetailsFragment {
@Override
protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
return super.use(clazz);
}
}
}

View File

@@ -19,12 +19,20 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
@@ -32,6 +40,8 @@ import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadow
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowEntityHeaderController;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.After;
@@ -45,8 +55,10 @@ import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -65,15 +77,21 @@ public class AudioStreamHeaderControllerTest {
@Mock private AudioStreamsHelper mAudioStreamsHelper;
@Mock private PreferenceScreen mScreen;
@Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private AudioStreamDetailsFragment mFragment;
@Mock private LayoutPreference mPreference;
@Mock private EntityHeaderController mHeaderController;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private AudioStreamHeaderController mController;
@Before
public void setUp() {
ShadowEntityHeaderController.setUseMock(mHeaderController);
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AudioStreamHeaderController(mContext, KEY);
mController.init(mFragment, BROADCAST_NAME, BROADCAST_ID);
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
@@ -87,6 +105,40 @@ public class AudioStreamHeaderControllerTest {
ShadowAudioStreamsHelper.reset();
}
@Test
public void onStart_registerCallbacks() {
mController.onStart(mLifecycleOwner);
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStart_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamHeaderController(mContext, KEY);
mController.onStart(mLifecycleOwner);
verify(mAssistant, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_unregisterCallbacks() {
mController.onStop(mLifecycleOwner);
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamHeaderController(mContext, KEY);
mController.onStop(mLifecycleOwner);
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void testDisplayPreference_sourceConnected_setSummary() {
when(mAudioStreamsHelper.getAllConnectedSources())
@@ -96,9 +148,11 @@ public class AudioStreamHeaderControllerTest {
mController.displayPreference(mScreen);
verify(mHeaderController).setLabel(BROADCAST_NAME);
verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController)
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY));
verify(mHeaderController).done(true);
verify(mScreen).addPreference(any());
}
@Test
@@ -108,7 +162,54 @@ public class AudioStreamHeaderControllerTest {
mController.displayPreference(mScreen);
verify(mHeaderController).setLabel(BROADCAST_NAME);
verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController).done(true);
verify(mScreen).addPreference(any());
}
@Test
public void testCallback_onSourceRemoved_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceRemoved(
mock(BluetoothDevice.class), /* sourceId= */ 0, /* reason= */ 0);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2)).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController, times(2)).done(true);
}
@Test
public void testCallback_onSourceLost_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2)).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController, times(2)).done(true);
}
@Test
public void testCallback_onReceiveStateChanged_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(state.getBisSyncState()).thenReturn(bisSyncState);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mock(BluetoothDevice.class), /* sourceId= */ 0, state);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2))
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY));
verify(mHeaderController, times(2)).done(true);
}
}

View File

@@ -22,18 +22,25 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
@@ -53,12 +60,14 @@ public class AudioStreamsDashboardFragmentTest {
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
private AudioStreamsProgressCategoryController mController;
private TestFragment mTestFragment;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mFeatureFactory = FakeFeatureFactory.setupForTest();
mTestFragment = spy(new TestFragment());
doReturn(mContext).when(mTestFragment).getContext();
mController = spy(new AudioStreamsProgressCategoryController(mContext, "key"));
@@ -114,6 +123,28 @@ public class AudioStreamsDashboardFragmentTest {
mTestFragment.onActivityResult(
REQUEST_SCAN_BT_BROADCAST_QR_CODE, Activity.RESULT_OK, intent);
verify(mController).setSourceFromQrCode(any(), any());
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(),
eq(SettingsEnums.ACTION_AUDIO_STREAM_QR_CODE_SCAN_SUCCEED),
anyInt());
}
@Test
public void onAttach_hasArgument() {
BluetoothLeBroadcastMetadata data = mock(BluetoothLeBroadcastMetadata.class);
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_BROADCAST_METADATA, data);
mTestFragment.setArguments(bundle);
mTestFragment.onAttach(mContext);
verify(mController).setSourceFromQrCode(eq(data), any());
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(),
eq(SettingsEnums.ACTION_AUDIO_STREAM_QR_CODE_SCAN_SUCCEED),
anyInt());
}
public static class TestFragment extends AudioStreamsDashboardFragment {

View File

@@ -73,7 +73,7 @@ public class AudioStreamsDialogFragmentTest {
}
@Test
public void testShowDialog() {
public void testShowDialog_dismissAll() {
FragmentController.setupFragment(mFragment);
AudioStreamsDialogFragment.show(mFragment, mDialogBuilder, SettingsEnums.PAGE_UNKNOWN);
ShadowLooper.idleMainLooper();
@@ -81,5 +81,8 @@ public class AudioStreamsDialogFragmentTest {
var dialog = ShadowAlertDialog.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
AudioStreamsDialogFragment.dismissAll(mFragment);
assertThat(dialog.isShowing()).isFalse();
}
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 2024 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.settings.connecteddevice.audiosharing.audiostreams;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowThreadUtils.class,
})
public class AudioStreamsHelperTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final int GROUP_ID = 1;
private static final int BROADCAST_ID_1 = 1;
private static final int BROADCAST_ID_2 = 2;
private static final String BROADCAST_NAME = "name";
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
@Mock private LocalBluetoothManager mLocalBluetoothManager;
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private CachedBluetoothDeviceManager mDeviceManager;
@Mock private BluetoothLeBroadcastMetadata mMetadata;
@Mock private CachedBluetoothDevice mCachedDevice;
@Mock private BluetoothDevice mDevice;
private AudioStreamsHelper mHelper;
@Before
public void setUp() {
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile())
.thenReturn(mAssistant);
mHelper = spy(new AudioStreamsHelper(mLocalBluetoothManager));
}
@Test
public void addSource_noDevice_doNothing() {
when(mAssistant.getDevicesMatchingConnectionStates(any()))
.thenReturn(Collections.emptyList());
mHelper.addSource(mMetadata);
verify(mAssistant, never()).addSource(any(), any(), anyBoolean());
}
@Test
public void addSource_hasDevice() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
mHelper.addSource(mMetadata);
verify(mAssistant).addSource(eq(mDevice), eq(mMetadata), anyBoolean());
}
@Test
public void removeSource_noDevice_doNothing() {
when(mAssistant.getDevicesMatchingConnectionStates(any()))
.thenReturn(Collections.emptyList());
mHelper.removeSource(BROADCAST_ID_1);
verify(mAssistant, never()).removeSource(any(), anyInt());
}
@Test
public void removeSource_noConnectedSource_doNothing() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
when(source.getBroadcastId()).thenReturn(BROADCAST_ID_2);
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(source));
mHelper.removeSource(BROADCAST_ID_1);
verify(mAssistant, never()).removeSource(any(), anyInt());
}
@Test
public void removeSource_hasConnectedSource() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
when(source.getBroadcastId()).thenReturn(BROADCAST_ID_2);
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(source));
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(source.getBisSyncState()).thenReturn(bisSyncState);
mHelper.removeSource(BROADCAST_ID_2);
verify(mAssistant).removeSource(eq(mDevice), anyInt());
}
@Test
public void removeSource_memberHasConnectedSource() {
List<BluetoothDevice> devices = new ArrayList<>();
var memberDevice = mock(BluetoothDevice.class);
devices.add(mDevice);
devices.add(memberDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
when(source.getBroadcastId()).thenReturn(BROADCAST_ID_2);
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
var memberCachedDevice = mock(CachedBluetoothDevice.class);
when(memberCachedDevice.getDevice()).thenReturn(memberDevice);
when(mCachedDevice.getMemberDevice()).thenReturn(ImmutableSet.of(memberCachedDevice));
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
when(mAssistant.getAllSources(mDevice)).thenReturn(ImmutableList.of());
when(mAssistant.getAllSources(memberDevice)).thenReturn(ImmutableList.of(source));
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(source.getBisSyncState()).thenReturn(bisSyncState);
mHelper.removeSource(BROADCAST_ID_2);
verify(mAssistant).removeSource(eq(memberDevice), anyInt());
}
@Test
public void getAllConnectedSources_noAssistant() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null);
mHelper = new AudioStreamsHelper(mLocalBluetoothManager);
assertThat(mHelper.getAllConnectedSources()).isEmpty();
}
@Test
public void getAllConnectedSources_returnSource() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(source));
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(source.getBisSyncState()).thenReturn(bisSyncState);
var list = mHelper.getAllConnectedSources();
assertThat(list).isNotEmpty();
assertThat(list.get(0)).isEqualTo(source);
}
@Test
public void startMediaService_noDevice_doNothing() {
mHelper.startMediaService(mContext, BROADCAST_ID_1, BROADCAST_NAME);
verify(mContext, never()).startService(any());
}
@Test
public void startMediaService_hasDevice() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
BluetoothLeBroadcastReceiveState source = mock(BluetoothLeBroadcastReceiveState.class);
when(mDeviceManager.findDevice(any())).thenReturn(mCachedDevice);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getGroupId()).thenReturn(GROUP_ID);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(source));
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(source.getBisSyncState()).thenReturn(bisSyncState);
mHelper.startMediaService(mContext, BROADCAST_ID_1, BROADCAST_NAME);
verify(mContext).startService(any());
}
}

View File

@@ -167,6 +167,25 @@ public class AudioStreamsProgressCategoryControllerTest {
verify(mPreference).setProgress(true);
}
@Test
public void testShowToast_noError() {
mController.showToast(BROADCAST_NAME_1);
}
@Test
public void testOnStop_unregister() {
mController.onStop(mLifecycleOwner);
verify(mBluetoothEventManager).unregisterCallback(any());
}
@Test
public void testGetFragment_returnFragment() {
mController.setFragment(mFragment);
assertThat(mController.getFragment()).isEqualTo(mFragment);
}
@Test
public void testOnStart_initNoDevice_showDialog() {
when(mLeBroadcastAssistant.isSearchInProgress()).thenReturn(true);
@@ -244,6 +263,25 @@ public class AudioStreamsProgressCategoryControllerTest {
verify(mController, never()).moveToState(any(), any());
}
@Test
public void testOnStart_initHasDevice_scanningInProgress() {
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
when(mLeBroadcastAssistant.isSearchInProgress()).thenReturn(true);
mController.onStart(mLifecycleOwner);
shadowOf(Looper.getMainLooper()).idle();
verify(mLeBroadcastAssistant).registerServiceCallBack(any(), any());
verify(mLeBroadcastAssistant).stopSearchingForSources();
verify(mLeBroadcastAssistant).startSearchingForSources(any());
var dialog = ShadowAlertDialog.getLatestAlertDialog();
assertThat(dialog).isNull();
verify(mController, never()).moveToState(any(), any());
}
@Test
public void testOnStart_handleSourceFromQrCode() {
// Setup a device
@@ -382,6 +420,49 @@ public class AudioStreamsProgressCategoryControllerTest {
assertThat(state.getValue()).isEqualTo(SYNCED);
}
@Test
public void testHandleSourceAddRequest_updateMetadataAndState() {
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
var metadata =
BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(VALID_METADATA);
assertThat(metadata).isNotNull();
var metadataWithNoCode =
new BluetoothLeBroadcastMetadata.Builder(metadata)
.setBroadcastId(NEWLY_FOUND_BROADCAST_ID)
.setBroadcastName(BROADCAST_NAME_1)
.build();
// A new source is found
mController.handleSourceFound(metadataWithNoCode);
ArgumentCaptor<AudioStreamPreference> preferenceCaptor =
ArgumentCaptor.forClass(AudioStreamPreference.class);
ArgumentCaptor<AudioStreamsProgressCategoryController.AudioStreamState> stateCaptor =
ArgumentCaptor.forClass(
AudioStreamsProgressCategoryController.AudioStreamState.class);
// moving state to SYNCED
verify(mController).moveToState(preferenceCaptor.capture(), stateCaptor.capture());
var preference = preferenceCaptor.getValue();
var state = stateCaptor.getValue();
assertThat(preference).isNotNull();
assertThat(preference.getAudioStreamBroadcastId()).isEqualTo(NEWLY_FOUND_BROADCAST_ID);
assertThat(state).isEqualTo(SYNCED);
var updatedMetadata =
new BluetoothLeBroadcastMetadata.Builder(metadataWithNoCode)
.setBroadcastCode(BROADCAST_CODE)
.build();
mController.handleSourceAddRequest(preference, updatedMetadata);
// state updated to ADD_SOURCE_WAIT_FOR_RESPONSE
assertThat(preference.getAudioStreamBroadcastId()).isEqualTo(NEWLY_FOUND_BROADCAST_ID);
assertThat(preference.getAudioStreamMetadata().getBroadcastCode())
.isEqualTo(BROADCAST_CODE);
assertThat(preference.getAudioStreamState()).isEqualTo(ADD_SOURCE_WAIT_FOR_RESPONSE);
}
@Test
public void testHandleSourceFound_sameIdWithSourceFromQrCode_updateMetadataAndState() {
// Setup a device
@@ -518,6 +599,42 @@ public class AudioStreamsProgressCategoryControllerTest {
.isEqualTo(NEWLY_FOUND_BROADCAST_ID);
}
@Test
public void testHandleSourceLost_sourceConnected_doNothing() {
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
// Setup mPreference so it's not null
mController.displayPreference(mScreen);
// A new source found
when(mMetadata.getBroadcastId()).thenReturn(NEWLY_FOUND_BROADCAST_ID);
mController.handleSourceFound(mMetadata);
shadowOf(Looper.getMainLooper()).idle();
// A new source found is lost, but the source is still connected
BluetoothLeBroadcastReceiveState connected = createConnectedMock(NEWLY_FOUND_BROADCAST_ID);
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
mController.handleSourceLost(NEWLY_FOUND_BROADCAST_ID);
shadowOf(Looper.getMainLooper()).idle();
ArgumentCaptor<AudioStreamPreference> preferenceToAdd =
ArgumentCaptor.forClass(AudioStreamPreference.class);
ArgumentCaptor<AudioStreamsProgressCategoryController.AudioStreamState> state =
ArgumentCaptor.forClass(
AudioStreamsProgressCategoryController.AudioStreamState.class);
// Verify a new preference is created with state SYNCED.
verify(mController).moveToState(preferenceToAdd.capture(), state.capture());
assertThat(preferenceToAdd.getValue()).isNotNull();
assertThat(preferenceToAdd.getValue().getAudioStreamBroadcastId())
.isEqualTo(NEWLY_FOUND_BROADCAST_ID);
assertThat(state.getValue()).isEqualTo(SYNCED);
// No preference is removed.
verify(mPreference, never()).removePreference(any());
}
@Test
public void testHandleSourceRemoved_removed() {
// Setup a device

View File

@@ -53,6 +53,8 @@ public class AudioStreamsProgressCategoryPreferenceTest {
@Test
public void addAudioStreamPreference_singlePreference() {
mPreference = spy(new AudioStreamsProgressCategoryPreference(mContext, null));
when(mPreference.getPreferenceManager()).thenReturn(mPreferenceManager);
AudioStreamPreference first = new AudioStreamPreference(mContext, null);
mPreference.addAudioStreamPreference(first, (p1, p2) -> 0);
@@ -62,6 +64,8 @@ public class AudioStreamsProgressCategoryPreferenceTest {
@Test
public void addAudioStreamPreference_multiPreference_sorted() {
mPreference = spy(new AudioStreamsProgressCategoryPreference(mContext, null, 0));
when(mPreference.getPreferenceManager()).thenReturn(mPreferenceManager);
Comparator<AudioStreamPreference> c =
Comparator.comparingInt(AudioStreamPreference::getOrder);
AudioStreamPreference first = new AudioStreamPreference(mContext, null);
@@ -78,6 +82,8 @@ public class AudioStreamsProgressCategoryPreferenceTest {
@Test
public void removeAudioStreamPreferences_shouldBeEmpty() {
mPreference = spy(new AudioStreamsProgressCategoryPreference(mContext, null, 0, 0));
when(mPreference.getPreferenceManager()).thenReturn(mPreferenceManager);
Comparator<AudioStreamPreference> c =
Comparator.comparingInt(AudioStreamPreference::getOrder);
AudioStreamPreference first = new AudioStreamPreference(mContext, null);

View File

@@ -97,6 +97,22 @@ public class AudioStreamsQrCodeFragmentTest {
assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.AUDIO_STREAM_QR_CODE);
}
@Test
public void onCreateView_noProfile_noQrCode() {
when(mBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
FragmentController.setupFragment(
mFragment, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
View view = mFragment.getView();
assertThat(view).isNotNull();
ImageView qrCodeView = view.findViewById(R.id.qrcode_view);
TextView passwordView = view.requireViewById(R.id.password);
assertThat(qrCodeView).isNotNull();
assertThat(qrCodeView.getDrawable()).isNull();
assertThat(passwordView).isNotNull();
assertThat(passwordView.getText().toString()).isEqualTo("");
}
@Test
public void onCreateView_noMetadata_noQrCode() {
List<BluetoothLeBroadcastMetadata> list = new ArrayList<>();

View File

@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import androidx.annotation.Nullable;
@@ -69,4 +70,16 @@ public class ShadowAudioStreamsHelper {
public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() {
return sMockHelper.getLeBroadcastAssistant();
}
/** Removes sources from LE broadcasts associated for all active sinks based on broadcast Id. */
@Implementation
public void removeSource(int broadcastId) {
sMockHelper.removeSource(broadcastId);
}
/** Adds the specified LE broadcast source to all active sinks. */
@Implementation
public void addSource(BluetoothLeBroadcastMetadata source) {
sMockHelper.addSource(source);
}
}