[Audiosharing] Created test for the name and password preferences.

Test: atest -c com.android.settings.connecteddevice.audiosharing
Flag: com.android.settingslib.flags.enable_le_audio_qr_code_private_broadcast_sharing
Bug: 345686602
Change-Id: I9036e560e29aba9555c207dce1b2018be010bca8
This commit is contained in:
chelseahao
2024-06-24 14:33:32 +08:00
parent e0abdc5c7a
commit 4afef7eed1
9 changed files with 1200 additions and 18 deletions

View File

@@ -95,14 +95,14 @@ public class AudioSharingNamePreference extends ValidatedEditTextPreference {
} }
private void configureInvisibleStateForQrCodeIcon(ImageButton shareButton, View divider) { private void configureInvisibleStateForQrCodeIcon(ImageButton shareButton, View divider) {
divider.setVisibility(View.INVISIBLE); divider.setVisibility(View.GONE);
shareButton.setVisibility(View.INVISIBLE); shareButton.setVisibility(View.GONE);
shareButton.setOnClickListener(null); shareButton.setOnClickListener(null);
} }
private void launchAudioSharingQrCodeFragment() { private void launchAudioSharingQrCodeFragment() {
new SubSettingLauncher(getContext()) new SubSettingLauncher(getContext())
.setTitleText(getContext().getString(R.string.audio_streams_qr_code_page_title)) .setTitleRes(R.string.audio_streams_qr_code_page_title)
.setDestination(AudioStreamsQrCodeFragment.class.getName()) .setDestination(AudioStreamsQrCodeFragment.class.getName())
.setSourceMetricsCategory(SettingsEnums.AUDIO_SHARING_SETTINGS) .setSourceMetricsCategory(SettingsEnums.AUDIO_SHARING_SETTINGS)
.launch(); .launch();

View File

@@ -26,6 +26,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference; import androidx.preference.Preference;
@@ -56,7 +57,8 @@ public class AudioSharingNamePreferenceController extends BasePreferenceControll
private static final boolean DEBUG = BluetoothUtils.D; private static final boolean DEBUG = BluetoothUtils.D;
private static final String PREF_KEY = "audio_sharing_stream_name"; private static final String PREF_KEY = "audio_sharing_stream_name";
private final BluetoothLeBroadcast.Callback mBroadcastCallback = @VisibleForTesting
final BluetoothLeBroadcast.Callback mBroadcastCallback =
new BluetoothLeBroadcast.Callback() { new BluetoothLeBroadcast.Callback() {
@Override @Override
public void onBroadcastMetadataChanged( public void onBroadcastMetadataChanged(

View File

@@ -19,12 +19,19 @@ package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.isBroadcasting; import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.isBroadcasting;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
@@ -41,15 +48,19 @@ import java.nio.charset.StandardCharsets;
public class AudioSharingPasswordPreferenceController extends BasePreferenceController public class AudioSharingPasswordPreferenceController extends BasePreferenceController
implements ValidatedEditTextPreference.Validator, implements ValidatedEditTextPreference.Validator,
AudioSharingPasswordPreference.OnDialogEventListener { AudioSharingPasswordPreference.OnDialogEventListener,
DefaultLifecycleObserver {
private static final String TAG = "AudioSharingPasswordPreferenceController"; private static final String TAG = "AudioSharingPasswordPreferenceController";
private static final String PREF_KEY = "audio_sharing_stream_password"; private static final String PREF_KEY = "audio_sharing_stream_password";
private static final String SHARED_PREF_NAME = "audio_sharing_settings"; private static final String SHARED_PREF_NAME = "audio_sharing_settings";
private static final String SHARED_PREF_KEY = "default_password"; private static final String SHARED_PREF_KEY = "default_password";
@Nullable private final ContentResolver mContentResolver;
@Nullable private final SharedPreferences mSharedPref;
@Nullable private final LocalBluetoothManager mBtManager; @Nullable private final LocalBluetoothManager mBtManager;
@Nullable private final LocalBluetoothLeBroadcast mBroadcast; @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
@Nullable private AudioSharingPasswordPreference mPreference; @Nullable private AudioSharingPasswordPreference mPreference;
private final ContentObserver mSettingsObserver;
private final SharedPreferences.OnSharedPreferenceChangeListener mSharedPrefChangeListener;
private final AudioSharingPasswordValidator mAudioSharingPasswordValidator; private final AudioSharingPasswordValidator mAudioSharingPasswordValidator;
private final MetricsFeatureProvider mMetricsFeatureProvider; private final MetricsFeatureProvider mMetricsFeatureProvider;
@@ -61,9 +72,44 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont
? mBtManager.getProfileManager().getLeAudioBroadcastProfile() ? mBtManager.getProfileManager().getLeAudioBroadcastProfile()
: null; : null;
mAudioSharingPasswordValidator = new AudioSharingPasswordValidator(); mAudioSharingPasswordValidator = new AudioSharingPasswordValidator();
mContentResolver = context.getContentResolver();
mSettingsObserver = new PasswordSettingsObserver();
mSharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
mSharedPrefChangeListener = new PasswordSharedPrefChangeListener();
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
} }
@Override
public void onStart(@NonNull LifecycleOwner owner) {
if (!isAvailable()) {
Log.d(TAG, "Feature is not available.");
return;
}
if (mContentResolver != null) {
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE),
false,
mSettingsObserver);
}
if (mSharedPref != null) {
mSharedPref.registerOnSharedPreferenceChangeListener(mSharedPrefChangeListener);
}
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
if (!isAvailable()) {
Log.d(TAG, "Feature is not available.");
return;
}
if (mContentResolver != null) {
mContentResolver.unregisterContentObserver(mSettingsObserver);
}
if (mSharedPref != null) {
mSharedPref.unregisterOnSharedPreferenceChangeListener(mSharedPrefChangeListener);
}
}
@Override @Override
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
@@ -125,7 +171,6 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont
persistDefaultPassword(mContext, password); persistDefaultPassword(mContext, password);
mBroadcast.setBroadcastCode( mBroadcast.setBroadcastCode(
isPublicBroadcast ? new byte[0] : password.getBytes()); isPublicBroadcast ? new byte[0] : password.getBytes());
updatePreference();
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
mContext, mContext,
SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED,
@@ -164,32 +209,52 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont
}); });
} }
private static void persistDefaultPassword(Context context, String defaultPassword) { private class PasswordSettingsObserver extends ContentObserver {
PasswordSettingsObserver() {
super(new Handler(Looper.getMainLooper()));
}
@Override
public void onChange(boolean selfChange) {
Log.d(TAG, "onChange, broadcast password has been changed");
updatePreference();
}
}
private class PasswordSharedPrefChangeListener
implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, @Nullable String key) {
if (!SHARED_PREF_KEY.equals(key)) {
return;
}
Log.d(TAG, "onSharedPreferenceChanged, default password has been changed");
updatePreference();
}
}
private void persistDefaultPassword(Context context, String defaultPassword) {
if (getDefaultPassword(context).equals(defaultPassword)) { if (getDefaultPassword(context).equals(defaultPassword)) {
return; return;
} }
if (mSharedPref == null) {
SharedPreferences sharedPref =
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
if (sharedPref == null) {
Log.w(TAG, "persistDefaultPassword(): sharedPref is empty!"); Log.w(TAG, "persistDefaultPassword(): sharedPref is empty!");
return; return;
} }
SharedPreferences.Editor editor = sharedPref.edit(); SharedPreferences.Editor editor = mSharedPref.edit();
editor.putString(SHARED_PREF_KEY, defaultPassword); editor.putString(SHARED_PREF_KEY, defaultPassword);
editor.apply(); editor.apply();
} }
private static String getDefaultPassword(Context context) { private String getDefaultPassword(Context context) {
SharedPreferences sharedPref = if (mSharedPref == null) {
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
if (sharedPref == null) {
Log.w(TAG, "getDefaultPassword(): sharedPref is empty!"); Log.w(TAG, "getDefaultPassword(): sharedPref is empty!");
return ""; return "";
} }
String value = sharedPref.getString(SHARED_PREF_KEY, ""); String value = mSharedPref.getString(SHARED_PREF_KEY, "");
if (value != null && value.isEmpty()) { if (value != null && value.isEmpty()) {
Log.w(TAG, "getDefaultPassword(): default password is empty!"); Log.w(TAG, "getDefaultPassword(): default password is empty!");
} }

View File

@@ -0,0 +1,319 @@
/*
* 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;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
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.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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 android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.bluetooth.Utils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
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.core.lifecycle.Lifecycle;
import com.android.settingslib.flags.Flags;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowLooper;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
})
public class AudioSharingNamePreferenceControllerTest {
private static final String PREF_KEY = "audio_sharing_stream_name";
private static final String BROADCAST_NAME = "broadcast_name";
private static final CharSequence UPDATED_NAME = "updated_name";
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Spy Context mContext = ApplicationProvider.getApplicationContext();
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private VolumeControlProfile mVolumeControl;
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private BluetoothEventManager mEventManager;
@Mock private LocalBluetoothProfileManager mProfileManager;
@Mock private PreferenceScreen mScreen;
private AudioSharingNamePreferenceController mController;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private AudioSharingNamePreference mPreference;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
when(mLocalBtManager.getEventManager()).thenReturn(mEventManager);
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
when(mBroadcast.isProfileReady()).thenReturn(true);
when(mAssistant.isProfileReady()).thenReturn(true);
when(mVolumeControl.isProfileReady()).thenReturn(true);
when(mBroadcast.isProfileReady()).thenReturn(true);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AudioSharingNamePreferenceController(mContext, PREF_KEY);
mPreference = spy(new AudioSharingNamePreference(mContext));
when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
}
@Test
public void getAvailabilityStatus_flagOn_available() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_flagOff_unsupported() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void onStart_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
verify(mBroadcast, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcast.Callback.class));
}
@Test
public void onStart_flagOn_registerCallbacks() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
verify(mBroadcast)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcast.Callback.class));
}
@Test
public void onStart_flagOn_serviceNotReady_registerCallbacks() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.isProfileReady()).thenReturn(false);
mController.onStart(mLifecycleOwner);
verify(mProfileManager)
.addServiceListener(any(LocalBluetoothProfileManager.ServiceListener.class));
}
@Test
public void onServiceConnected_removeCallbacks() {
mController.onServiceConnected();
verify(mProfileManager)
.removeServiceListener(any(LocalBluetoothProfileManager.ServiceListener.class));
}
@Test
public void onStop_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
mController.onStop(mLifecycleOwner);
verify(mBroadcast, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
}
@Test
public void onStop_flagOn_unregisterCallbacks() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
mController.onStop(mLifecycleOwner);
verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
}
@Test
public void displayPreference_updateName_showIcon() {
when(mBroadcast.getBroadcastName()).thenReturn(BROADCAST_NAME);
when(mBroadcast.isEnabled(any())).thenReturn(true);
mController.displayPreference(mScreen);
ShadowLooper.idleMainLooper();
assertThat(mPreference.getText()).isEqualTo(BROADCAST_NAME);
assertThat(mPreference.getSummary()).isEqualTo(BROADCAST_NAME);
verify(mPreference).setValidator(any());
verify(mPreference).setShowQrCodeIcon(true);
}
@Test
public void displayPreference_updateName_hideIcon() {
when(mBroadcast.getBroadcastName()).thenReturn(BROADCAST_NAME);
when(mBroadcast.isEnabled(any())).thenReturn(false);
mController.displayPreference(mScreen);
ShadowLooper.idleMainLooper();
assertThat(mPreference.getText()).isEqualTo(BROADCAST_NAME);
assertThat(mPreference.getSummary()).isEqualTo(BROADCAST_NAME);
verify(mPreference).setValidator(any());
verify(mPreference).setShowQrCodeIcon(false);
}
@Test
public void onPreferenceChange_noChange_doNothing() {
when(mPreference.getSummary()).thenReturn(BROADCAST_NAME);
mController.displayPreference(mScreen);
boolean changed = mController.onPreferenceChange(mPreference, BROADCAST_NAME);
ShadowLooper.idleMainLooper();
verify(mBroadcast, never()).setBroadcastName(anyString());
verify(mBroadcast, never()).setProgramInfo(anyString());
verify(mBroadcast, never()).updateBroadcast();
verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt());
assertThat(changed).isFalse();
}
@Test
public void onPreferenceChange_changed_updateName_broadcasting() {
when(mPreference.getSummary()).thenReturn(BROADCAST_NAME);
when(mBroadcast.isEnabled(any())).thenReturn(true);
mController.displayPreference(mScreen);
boolean changed = mController.onPreferenceChange(mPreference, UPDATED_NAME);
ShadowLooper.idleMainLooper();
verify(mBroadcast).setBroadcastName(UPDATED_NAME.toString());
verify(mBroadcast).setProgramInfo(UPDATED_NAME.toString());
verify(mBroadcast).updateBroadcast();
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_NAME_UPDATED, 1);
assertThat(changed).isTrue();
}
@Test
public void onPreferenceChange_changed_updateName_notBroadcasting() {
when(mPreference.getSummary()).thenReturn(BROADCAST_NAME);
when(mBroadcast.isEnabled(any())).thenReturn(false);
mController.displayPreference(mScreen);
boolean changed = mController.onPreferenceChange(mPreference, UPDATED_NAME);
ShadowLooper.idleMainLooper();
verify(mBroadcast).setBroadcastName(UPDATED_NAME.toString());
verify(mBroadcast).setProgramInfo(UPDATED_NAME.toString());
verify(mBroadcast, never()).updateBroadcast();
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_NAME_UPDATED, 0);
assertThat(changed).isTrue();
}
@Test
public void unrelatedCallbacks_doNotUpdateIcon() {
mController.displayPreference(mScreen);
mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 0);
mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 0, /* broadcastId= */ 0);
mController.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 0);
mController.mBroadcastCallback.onBroadcastUpdateFailed(
/* reason= */ 0, /* broadcastId= */ 0);
mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 0, /* broadcastId= */ 0);
mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 0, /* broadcastId= */ 0);
mController.mBroadcastCallback.onPlaybackStopped(/* reason= */ 0, /* broadcastId= */ 0);
ShadowLooper.idleMainLooper();
// Should be called once in displayPreference, but not called after callbacks
verify(mPreference).setShowQrCodeIcon(anyBoolean());
}
@Test
public void broadcastOnCallback_updateIcon() {
mController.displayPreference(mScreen);
mController.mBroadcastCallback.onBroadcastMetadataChanged(
/* broadcastId= */ 0, mock(BluetoothLeBroadcastMetadata.class));
ShadowLooper.idleMainLooper();
// Should be called twice, in displayPreference and also after callback
verify(mPreference, times(2)).setShowQrCodeIcon(anyBoolean());
}
@Test
public void broadcastStopCallback_updateIcon() {
mController.displayPreference(mScreen);
mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 0, /* broadcastId= */ 0);
ShadowLooper.idleMainLooper();
// Should be called twice, in displayPreference and also after callback
verify(mPreference, times(2)).setShowQrCodeIcon(anyBoolean());
}
@Test
public void idTextValid_emptyString() {
boolean valid = mController.isTextValid("");
assertThat(valid).isFalse();
}
@Test
public void idTextValid_validName() {
boolean valid = mController.isTextValid("valid name");
assertThat(valid).isTrue();
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioSharingNamePreferenceTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private Context mContext;
private AudioSharingNamePreference mPreference;
@Before
public void setup() {
mContext = ApplicationProvider.getApplicationContext();
mPreference = spy(new AudioSharingNamePreference(mContext, null));
}
@Test
public void initialize_correctLayout() {
assertThat(mPreference.getLayoutResource())
.isEqualTo(
com.android.settingslib.widget.preference.twotarget.R.layout
.preference_two_target);
assertThat(mPreference.getWidgetLayoutResource())
.isEqualTo(R.layout.preference_widget_qrcode);
}
@Test
public void onBindViewHolder_correctLayout_noQrCodeButton() {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(mPreference.getLayoutResource(), null);
LinearLayout widgetView = view.findViewById(android.R.id.widget_frame);
assertThat(widgetView).isNotNull();
inflater.inflate(mPreference.getWidgetLayoutResource(), widgetView, true);
var holder = PreferenceViewHolder.createInstanceForTests(view);
mPreference.setShowQrCodeIcon(false);
mPreference.onBindViewHolder(holder);
ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon);
View divider =
holder.findViewById(
com.android.settingslib.widget.preference.twotarget.R.id
.two_target_divider);
assertThat(shareButton).isNotNull();
assertThat(shareButton.getVisibility()).isEqualTo(View.GONE);
assertThat(shareButton.hasOnClickListeners()).isFalse();
assertThat(divider).isNotNull();
assertThat(divider.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onBindViewHolder_correctLayout_showQrCodeButton() {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(mPreference.getLayoutResource(), null);
LinearLayout widgetView = view.findViewById(android.R.id.widget_frame);
assertThat(widgetView).isNotNull();
inflater.inflate(mPreference.getWidgetLayoutResource(), widgetView, true);
var holder = PreferenceViewHolder.createInstanceForTests(view);
mPreference.setShowQrCodeIcon(true);
mPreference.onBindViewHolder(holder);
ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon);
View divider =
holder.findViewById(
com.android.settingslib.widget.preference.twotarget.R.id
.two_target_divider);
assertThat(shareButton).isNotNull();
assertThat(shareButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(shareButton.getDrawable()).isNotNull();
assertThat(shareButton.hasOnClickListeners()).isTrue();
assertThat(divider).isNotNull();
assertThat(divider.getVisibility()).isEqualTo(View.VISIBLE);
// mContext is not an Activity context, calling startActivity() from outside of an Activity
// context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this
// AndroidRuntimeException.
Context activityContext = mock(Context.class);
when(mPreference.getContext()).thenReturn(activityContext);
shareButton.callOnClick();
ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(activityContext).startActivity(argumentCaptor.capture());
Intent intent = argumentCaptor.getValue();
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(AudioStreamsQrCodeFragment.class.getName());
assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
.isEqualTo(R.string.audio_streams_qr_code_page_title);
assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0))
.isEqualTo(SettingsEnums.AUDIO_SHARING_SETTINGS);
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioSharingNameTextValidatorTest {
private AudioSharingNameTextValidator mValidator;
@Before
public void setUp() {
mValidator = new AudioSharingNameTextValidator();
}
@Test
public void testValidNames() {
assertThat(mValidator.isTextValid("ValidName")).isTrue();
assertThat(mValidator.isTextValid("12345678")).isTrue();
assertThat(mValidator.isTextValid("Name_With_Underscores")).isTrue();
assertThat(mValidator.isTextValid("ÄÖÜß")).isTrue();
assertThat(mValidator.isTextValid("ThisNameIsExactly32Characters!")).isTrue();
}
@Test
public void testInvalidNames() {
assertThat(mValidator.isTextValid(null)).isFalse();
assertThat(mValidator.isTextValid("")).isFalse();
assertThat(mValidator.isTextValid("abc")).isFalse();
assertThat(mValidator.isTextValid("ThisNameIsWayTooLongForAnAudioSharingName")).isFalse();
assertThat(mValidator.isTextValid("Invalid\uDC00")).isFalse();
}
}

View File

@@ -0,0 +1,335 @@
/*
* 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;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
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.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.testutils.FakeFeatureFactory;
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.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.flags.Flags;
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.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowLooper;
import java.nio.charset.StandardCharsets;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
})
public class AudioSharingPasswordPreferenceControllerTest {
private static final String PREF_KEY = "audio_sharing_stream_password";
private static final String SHARED_PREF_KEY = "default_password";
private static final String BROADCAST_PASSWORD = "password";
private static final String EDITTEXT_PASSWORD = "edittext_password";
private static final String HIDDEN_PASSWORD = "********";
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Spy Context mContext = ApplicationProvider.getApplicationContext();
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private LocalBluetoothProfileManager mProfileManager;
@Mock private SharedPreferences mSharedPreferences;
@Mock private SharedPreferences.Editor mEditor;
@Mock private ContentResolver mContentResolver;
@Mock private PreferenceScreen mScreen;
private AudioSharingPasswordPreferenceController mController;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private AudioSharingPasswordPreference mPreference;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mLocalBtManager = Utils.getLocalBtManager(mContext);
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
when(mSharedPreferences.edit()).thenReturn(mEditor);
when(mEditor.putString(anyString(), anyString())).thenReturn(mEditor);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AudioSharingPasswordPreferenceController(mContext, PREF_KEY);
mPreference = spy(new AudioSharingPasswordPreference(mContext));
when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
}
@Test
public void getAvailabilityStatus_flagOn_available() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_flagOff_unsupported() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void onStart_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
verify(mContentResolver, never()).registerContentObserver(any(), anyBoolean(), any());
verify(mSharedPreferences, never()).registerOnSharedPreferenceChangeListener(any());
}
@Test
public void onStart_flagOn_registerCallbacks() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
verify(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(any());
}
@Test
public void onStop_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStop(mLifecycleOwner);
verify(mContentResolver, never()).unregisterContentObserver(any());
verify(mSharedPreferences, never()).unregisterOnSharedPreferenceChangeListener(any());
}
@Test
public void onStop_flagOn_registerCallbacks() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStop(mLifecycleOwner);
verify(mContentResolver).unregisterContentObserver(any());
verify(mSharedPreferences).unregisterOnSharedPreferenceChangeListener(any());
}
@Test
public void displayPreference_setupPreference_noPassword() {
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
mController.displayPreference(mScreen);
ShadowLooper.idleMainLooper();
assertThat(mPreference.isPassword()).isTrue();
assertThat(mPreference.getDialogLayoutResource())
.isEqualTo(R.layout.audio_sharing_password_dialog);
assertThat(mPreference.getText()).isEqualTo(EDITTEXT_PASSWORD);
assertThat(mPreference.getSummary())
.isEqualTo(mContext.getString(R.string.audio_streams_no_password_summary));
verify(mPreference).setValidator(any());
verify(mPreference).setOnDialogEventListener(any());
}
@Test
public void contentObserver_updatePreferenceOnChange() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.getBroadcastCode())
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
mController.onStart(mLifecycleOwner);
mController.displayPreference(mScreen);
ShadowLooper.idleMainLooper();
ArgumentCaptor<ContentObserver> observerCaptor =
ArgumentCaptor.forClass(ContentObserver.class);
verify(mContentResolver)
.registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
var observer = observerCaptor.getValue();
assertThat(observer).isNotNull();
observer.onChange(true);
verify(mPreference).setText(anyString());
verify(mPreference).setSummary(anyString());
}
@Test
public void sharedPrefChangeListener_updatePreferenceOnChange() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.getBroadcastCode())
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
mController.onStart(mLifecycleOwner);
mController.displayPreference(mScreen);
ShadowLooper.idleMainLooper();
ArgumentCaptor<SharedPreferences.OnSharedPreferenceChangeListener> captor =
ArgumentCaptor.forClass(SharedPreferences.OnSharedPreferenceChangeListener.class);
verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(captor.capture());
var observer = captor.getValue();
assertThat(captor).isNotNull();
observer.onSharedPreferenceChanged(mSharedPreferences, SHARED_PREF_KEY);
verify(mPreference).setText(anyString());
verify(mPreference).setSummary(anyString());
}
@Test
public void displayPreference_setupPreference_hasPassword() {
when(mBroadcast.getBroadcastCode())
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
mController.displayPreference(mScreen);
ShadowLooper.idleMainLooper();
assertThat(mPreference.isPassword()).isTrue();
assertThat(mPreference.getDialogLayoutResource())
.isEqualTo(R.layout.audio_sharing_password_dialog);
assertThat(mPreference.getText()).isEqualTo(BROADCAST_PASSWORD);
assertThat(mPreference.getSummary()).isEqualTo(HIDDEN_PASSWORD);
verify(mPreference).setValidator(any());
verify(mPreference).setOnDialogEventListener(any());
}
@Test
public void onBindDialogView_updatePreference_isBroadcasting_noPassword() {
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
when(mBroadcast.isEnabled(any())).thenReturn(true);
mController.displayPreference(mScreen);
mController.onBindDialogView();
ShadowLooper.idleMainLooper();
verify(mPreference).setEditable(false);
verify(mPreference).setChecked(true);
}
@Test
public void onBindDialogView_updatePreference_isNotBroadcasting_hasPassword() {
when(mBroadcast.getBroadcastCode())
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
mController.displayPreference(mScreen);
mController.onBindDialogView();
ShadowLooper.idleMainLooper();
verify(mPreference).setEditable(true);
verify(mPreference).setChecked(false);
}
@Test
public void onPreferenceDataChanged_isBroadcasting_doNothing() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
mController.displayPreference(mScreen);
mController.onPreferenceDataChanged(BROADCAST_PASSWORD, /* isPublicBroadcast= */ false);
ShadowLooper.idleMainLooper();
verify(mBroadcast, never()).setBroadcastCode(any());
verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt());
}
@Test
public void onPreferenceDataChanged_noChange_doNothing() {
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
mController.displayPreference(mScreen);
mController.onPreferenceDataChanged(EDITTEXT_PASSWORD, /* isPublicBroadcast= */ true);
ShadowLooper.idleMainLooper();
verify(mBroadcast, never()).setBroadcastCode(any());
verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt());
}
@Test
public void onPreferenceDataChanged_updateToNonPublicBroadcast() {
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
mController.displayPreference(mScreen);
mController.onPreferenceDataChanged(BROADCAST_PASSWORD, /* isPublicBroadcast= */ false);
ShadowLooper.idleMainLooper();
verify(mBroadcast).setBroadcastCode(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
verify(mEditor).putString(anyString(), eq(BROADCAST_PASSWORD));
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, 0);
}
@Test
public void onPreferenceDataChanged_updateToPublicBroadcast() {
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
when(mBroadcast.getBroadcastCode())
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
mController.displayPreference(mScreen);
mController.onPreferenceDataChanged(EDITTEXT_PASSWORD, /* isPublicBroadcast= */ true);
ShadowLooper.idleMainLooper();
verify(mBroadcast).setBroadcastCode("".getBytes(StandardCharsets.UTF_8));
verify(mEditor, never()).putString(anyString(), eq(EDITTEXT_PASSWORD));
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, 1);
}
@Test
public void idTextValid_emptyString() {
boolean valid = mController.isTextValid("");
assertThat(valid).isFalse();
}
@Test
public void idTextValid_validPassword() {
boolean valid = mController.isTextValid(BROADCAST_PASSWORD);
assertThat(valid).isTrue();
}
}

View File

@@ -0,0 +1,215 @@
/*
* 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;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioSharingPasswordPreferenceTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final String EDIT_TEXT_CONTENT = "text";
private Context mContext;
private AudioSharingPasswordPreference mPreference;
@Before
public void setup() {
mContext = ApplicationProvider.getApplicationContext();
mPreference = new AudioSharingPasswordPreference(mContext, null);
}
@Test
public void onBindDialogView_correctLayout() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
var editText = view.findViewById(android.R.id.edit);
var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
var dialogMessage = view.findViewById(android.R.id.message);
assertThat(editText).isNotNull();
assertThat(checkBox).isNotNull();
assertThat(dialogMessage).isNotNull();
}
@Test
public void setEditable_true() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
var editText = view.findViewById(android.R.id.edit);
var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
var dialogMessage = view.findViewById(android.R.id.message);
mPreference.setEditable(true);
assertThat(editText).isNotNull();
assertThat(editText.isEnabled()).isTrue();
assertThat(editText.getAlpha()).isEqualTo(1.0f);
assertThat(checkBox).isNotNull();
assertThat(checkBox.isEnabled()).isTrue();
assertThat(dialogMessage).isNotNull();
assertThat(dialogMessage.getVisibility()).isEqualTo(GONE);
}
@Test
public void setEditable_false() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
var editText = view.findViewById(android.R.id.edit);
var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
var dialogMessage = view.findViewById(android.R.id.message);
mPreference.setEditable(false);
assertThat(editText).isNotNull();
assertThat(editText.isEnabled()).isFalse();
assertThat(editText.getAlpha()).isLessThan(1.0f);
assertThat(checkBox).isNotNull();
assertThat(checkBox.isEnabled()).isFalse();
assertThat(dialogMessage).isNotNull();
assertThat(dialogMessage.getVisibility()).isEqualTo(VISIBLE);
}
@Test
public void setChecked_true() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
CheckBox checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
mPreference.setChecked(true);
assertThat(checkBox).isNotNull();
assertThat(checkBox.isChecked()).isTrue();
}
@Test
public void setChecked_false() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
CheckBox checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
mPreference.setChecked(false);
assertThat(checkBox).isNotNull();
assertThat(checkBox.isChecked()).isFalse();
}
@Test
public void onDialogEventListener_onClick_positiveButton() {
AudioSharingPasswordPreference.OnDialogEventListener listener =
mock(AudioSharingPasswordPreference.OnDialogEventListener.class);
mPreference.setOnDialogEventListener(listener);
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
EditText editText = view.findViewById(android.R.id.edit);
assertThat(editText).isNotNull();
editText.setText(EDIT_TEXT_CONTENT);
mPreference.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_POSITIVE);
verify(listener).onBindDialogView();
verify(listener).onPreferenceDataChanged(eq(EDIT_TEXT_CONTENT), anyBoolean());
}
@Test
public void onDialogEventListener_onClick_negativeButton_doNothing() {
AudioSharingPasswordPreference.OnDialogEventListener listener =
mock(AudioSharingPasswordPreference.OnDialogEventListener.class);
mPreference.setOnDialogEventListener(listener);
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
EditText editText = view.findViewById(android.R.id.edit);
assertThat(editText).isNotNull();
editText.setText(EDIT_TEXT_CONTENT);
mPreference.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_NEGATIVE);
verify(listener).onBindDialogView();
verify(listener, never()).onPreferenceDataChanged(anyString(), anyBoolean());
}
@Test
public void onPrepareDialogBuilder_editable_doNothing() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
mPreference.setEditable(true);
var dialogBuilder = mock(AlertDialog.Builder.class);
mPreference.onPrepareDialogBuilder(
dialogBuilder, mock(DialogInterface.OnClickListener.class));
verify(dialogBuilder, never()).setPositiveButton(any(), any());
}
@Test
public void onPrepareDialogBuilder_notEditable_disableButton() {
View view =
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
mPreference.onBindDialogView(view);
mPreference.setEditable(false);
var dialogBuilder = mock(AlertDialog.Builder.class);
mPreference.onPrepareDialogBuilder(
dialogBuilder, mock(DialogInterface.OnClickListener.class));
verify(dialogBuilder).setPositiveButton(any(), any());
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioSharingPasswordValidatorTest {
private AudioSharingPasswordValidator mValidator;
@Before
public void setUp() {
mValidator = new AudioSharingPasswordValidator();
}
@Test
public void testValidPasswords() {
assertThat(mValidator.isTextValid("1234")).isTrue();
assertThat(mValidator.isTextValid("Password")).isTrue();
assertThat(mValidator.isTextValid("SecurePass123!")).isTrue();
assertThat(mValidator.isTextValid("ÄÖÜß")).isTrue();
assertThat(mValidator.isTextValid("1234567890abcdef")).isTrue();
}
@Test
public void testInvalidPasswords() {
assertThat(mValidator.isTextValid(null)).isFalse();
assertThat(mValidator.isTextValid("")).isFalse();
assertThat(mValidator.isTextValid("abc")).isFalse();
assertThat(mValidator.isTextValid("ThisIsAVeryLongPasswordThatExceedsSixteenOctets"))
.isFalse();
assertThat(mValidator.isTextValid("Invalid\uDC00")).isFalse();
}
}