1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.development; 18 19 import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_KEY; 20 import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_OFF; 21 import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_ON; 22 import static com.android.settings.development.BluetoothLeAudioUiPreferenceController.VALUE_UNSET; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 import static org.robolectric.Shadows.shadowOf; 30 31 import android.bluetooth.BluetoothAdapter; 32 import android.bluetooth.BluetoothStatusCodes; 33 import android.content.Context; 34 import android.os.Looper; 35 import android.os.SystemProperties; 36 import android.platform.test.annotations.DisableFlags; 37 import android.platform.test.annotations.EnableFlags; 38 import android.platform.test.flag.junit.SetFlagsRule; 39 import android.provider.Settings; 40 41 import androidx.preference.PreferenceScreen; 42 import androidx.preference.SwitchPreferenceCompat; 43 44 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 45 import com.android.settingslib.flags.Flags; 46 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.Mock; 52 import org.mockito.junit.MockitoJUnit; 53 import org.mockito.junit.MockitoRule; 54 import org.robolectric.RobolectricTestRunner; 55 import org.robolectric.RuntimeEnvironment; 56 import org.robolectric.annotation.Config; 57 import org.robolectric.annotation.Implementation; 58 import org.robolectric.annotation.Implements; 59 import org.robolectric.shadow.api.Shadow; 60 61 @RunWith(RobolectricTestRunner.class) 62 @Config( 63 shadows = { 64 ShadowBluetoothAdapter.class, 65 BluetoothLeAudioUiPreferenceControllerTest.ShadowBluetoothRebootDialogFragment.class 66 }) 67 public class BluetoothLeAudioUiPreferenceControllerTest { 68 @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 69 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 70 private static final String SOURCE_SYSTEM_PROP_KEY = 71 "bluetooth.profile.bap.broadcast.source.enabled"; 72 private static final String ASSIST_SYSTEM_PROP_KEY = 73 "bluetooth.profile.bap.broadcast.assist.enabled"; 74 @Mock private PreferenceScreen mPreferenceScreen; 75 @Mock private DevelopmentSettingsDashboardFragment mFragment; 76 @Mock private SwitchPreferenceCompat mPreference; 77 private ShadowBluetoothAdapter mShadowBluetoothAdapter; 78 private Context mContext; 79 private BluetoothLeAudioUiPreferenceController mController; 80 81 @Before setup()82 public void setup() { 83 mContext = RuntimeEnvironment.getApplication(); 84 SystemProperties.set(SOURCE_SYSTEM_PROP_KEY, "true"); 85 SystemProperties.set(ASSIST_SYSTEM_PROP_KEY, "true"); 86 // Reset value 87 Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET); 88 mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); 89 mShadowBluetoothAdapter.setEnabled(true); 90 mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( 91 BluetoothStatusCodes.FEATURE_SUPPORTED); 92 mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( 93 BluetoothStatusCodes.FEATURE_SUPPORTED); 94 mController = spy(new BluetoothLeAudioUiPreferenceController(mContext, mFragment)); 95 when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) 96 .thenReturn(mPreference); 97 mController.displayPreference(mPreferenceScreen); 98 } 99 100 @Test 101 @DisableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) isAvailable_flagOff_returnFalse()102 public void isAvailable_flagOff_returnFalse() { 103 assertThat(mController.isAvailable()).isFalse(); 104 } 105 106 @Test 107 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) isAvailable_flagOn_returnFalse()108 public void isAvailable_flagOn_returnFalse() { 109 assertThat(mController.isAvailable()).isTrue(); 110 } 111 112 @Test 113 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) isAvailable_flagOn_propertyOff_returnFalse()114 public void isAvailable_flagOn_propertyOff_returnFalse() { 115 SystemProperties.set(SOURCE_SYSTEM_PROP_KEY, "false"); 116 assertThat(mController.isAvailable()).isFalse(); 117 } 118 119 @Test 120 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) updateState_settingEnabled_checked()121 public void updateState_settingEnabled_checked() { 122 Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_ON); 123 mController.updateState(mPreference); 124 shadowOf(Looper.getMainLooper()).idle(); 125 126 verify(mPreference).setChecked(true); 127 } 128 129 @Test 130 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) updateState_settingDisabled_notChecked()131 public void updateState_settingDisabled_notChecked() { 132 Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_OFF); 133 mController.updateState(mPreference); 134 shadowOf(Looper.getMainLooper()).idle(); 135 136 verify(mPreference).setChecked(false); 137 } 138 139 @Test 140 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) updateState_featureSupported_enabled()141 public void updateState_featureSupported_enabled() { 142 mController.updateState(mPreference); 143 shadowOf(Looper.getMainLooper()).idle(); 144 145 verify(mPreference).setEnabled(true); 146 } 147 148 @Test 149 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) updateState_featureUnsupported_disabled()150 public void updateState_featureUnsupported_disabled() { 151 mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( 152 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); 153 mController.updateState(mPreference); 154 shadowOf(Looper.getMainLooper()).idle(); 155 156 verify(mPreference).setEnabled(false); 157 } 158 159 @Test 160 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) onRebootDialogConfirmed_noChange_doNothing()161 public void onRebootDialogConfirmed_noChange_doNothing() { 162 mController.onRebootDialogConfirmed(); 163 164 int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET); 165 assertThat(result).isEqualTo(VALUE_UNSET); 166 } 167 168 @Test 169 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) onRebootDialogConfirmed_hasChange_turnOn()170 public void onRebootDialogConfirmed_hasChange_turnOn() { 171 mController.onPreferenceChange(mPreference, true); 172 mController.onRebootDialogConfirmed(); 173 174 int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET); 175 assertThat(result).isEqualTo(VALUE_ON); 176 } 177 178 @Test 179 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) onRebootDialogCanceled_hasChange_doNothing()180 public void onRebootDialogCanceled_hasChange_doNothing() { 181 mController.onPreferenceChange(mPreference, true); 182 mController.onRebootDialogCanceled(); 183 184 int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET); 185 assertThat(result).isEqualTo(VALUE_UNSET); 186 } 187 188 @Test 189 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) onBroadcastDisabled_currentValueOn_turnOff()190 public void onBroadcastDisabled_currentValueOn_turnOff() { 191 Settings.Global.putInt(mContext.getContentResolver(), VALUE_KEY, VALUE_ON); 192 mController.updateState(mPreference); 193 shadowOf(Looper.getMainLooper()).idle(); 194 mController.onBroadcastDisabled(); 195 196 int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET); 197 assertThat(result).isEqualTo(VALUE_OFF); 198 } 199 200 @Test 201 @EnableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION) onBroadcastDisabled_currentValueUnset_doNothing()202 public void onBroadcastDisabled_currentValueUnset_doNothing() { 203 mController.updateState(mPreference); 204 mController.onBroadcastDisabled(); 205 shadowOf(Looper.getMainLooper()).idle(); 206 207 int result = Settings.Global.getInt(mContext.getContentResolver(), VALUE_KEY, VALUE_UNSET); 208 assertThat(result).isEqualTo(VALUE_UNSET); 209 } 210 211 @Implements(BluetoothRebootDialog.class) 212 public static class ShadowBluetoothRebootDialogFragment { 213 214 /** Shadow implementation of BluetoothRebootDialog#show */ 215 @Implementation show(DevelopmentSettingsDashboardFragment host)216 public static void show(DevelopmentSettingsDashboardFragment host) { 217 // Do nothing. 218 } 219 } 220 } 221