1 /* 2 * Copyright (C) 2017 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.bluetooth; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.doReturn; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.spy; 24 import static org.mockito.Mockito.times; 25 import static org.mockito.Mockito.verify; 26 27 import android.bluetooth.BluetoothAdapter; 28 import android.content.Context; 29 import android.os.Bundle; 30 import android.view.View; 31 32 import androidx.annotation.NonNull; 33 import androidx.lifecycle.Lifecycle; 34 import androidx.lifecycle.LifecycleObserver; 35 import androidx.lifecycle.LifecycleOwner; 36 import androidx.test.core.app.ApplicationProvider; 37 38 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 39 import com.android.settingslib.bluetooth.LocalBluetoothManager; 40 import com.android.settingslib.widget.FooterPreference; 41 42 import org.junit.Before; 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Answers; 47 import org.mockito.Mock; 48 import org.mockito.junit.MockitoJUnit; 49 import org.mockito.junit.MockitoRule; 50 import org.robolectric.RobolectricTestRunner; 51 52 import java.util.Collections; 53 54 @RunWith(RobolectricTestRunner.class) 55 public class BluetoothPairingDetailTest { 56 @Rule 57 public final MockitoRule mockito = MockitoJUnit.rule(); 58 59 private final Context mContext = ApplicationProvider.getApplicationContext(); 60 61 private final Lifecycle mFakeLifecycle = new Lifecycle() { 62 @Override 63 public void addObserver(@NonNull LifecycleObserver observer) {} 64 65 @Override 66 public void removeObserver(@NonNull LifecycleObserver observer) {} 67 68 @NonNull 69 @Override 70 public State getCurrentState() { 71 return State.CREATED; 72 } 73 }; 74 75 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 76 private LocalBluetoothManager mLocalManager; 77 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 78 private CachedBluetoothDeviceManager mDeviceManager; 79 private BluetoothPairingDetail mFragment; 80 private BluetoothProgressCategory mAvailableDevicesCategory; 81 private FooterPreference mFooterPreference; 82 private BluetoothAdapter mBluetoothAdapter; 83 84 @Before setUp()85 public void setUp() { 86 mFragment = spy(new BluetoothPairingDetail()); 87 doReturn(mContext).when(mFragment).getContext(); 88 mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext)); 89 mFooterPreference = new FooterPreference(mContext); 90 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 91 92 doReturn(mAvailableDevicesCategory).when(mFragment) 93 .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES); 94 doReturn(mFooterPreference).when(mFragment) 95 .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF); 96 doReturn(new View(mContext)).when(mFragment).getView(); 97 doReturn((LifecycleOwner) () -> mFakeLifecycle).when(mFragment).getViewLifecycleOwner(); 98 doReturn(Collections.emptyList()).when(mDeviceManager).getCachedDevicesCopy(); 99 100 mFragment.mBluetoothAdapter = mBluetoothAdapter; 101 mFragment.mLocalManager = mLocalManager; 102 mFragment.mCachedDeviceManager = mDeviceManager; 103 mFragment.mDeviceListGroup = mAvailableDevicesCategory; 104 mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY); 105 } 106 107 @Test initPreferencesFromPreferenceScreen_findPreferences()108 public void initPreferencesFromPreferenceScreen_findPreferences() { 109 mFragment.initPreferencesFromPreferenceScreen(); 110 111 assertThat(mFragment.mAvailableDevicesCategory).isEqualTo(mAvailableDevicesCategory); 112 assertThat(mFragment.mFooterPreference).isEqualTo(mFooterPreference); 113 } 114 115 @Test updateContent_stateOn_addDevices()116 public void updateContent_stateOn_addDevices() { 117 mFragment.initPreferencesFromPreferenceScreen(); 118 119 mFragment.updateContent(BluetoothAdapter.STATE_ON); 120 121 assertThat(mFragment.mAlwaysDiscoverable.mStarted).isEqualTo(true); 122 assertThat(mBluetoothAdapter.getScanMode()) 123 .isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); 124 } 125 126 @Test onScanningStateChanged_restartScanAfterInitialScanning()127 public void onScanningStateChanged_restartScanAfterInitialScanning() { 128 mFragment.initPreferencesFromPreferenceScreen(); 129 130 // Initial Bluetooth ON will trigger scan enable, list clear and scan start 131 mFragment.updateContent(BluetoothAdapter.STATE_ON); 132 verify(mFragment).enableScanning(); 133 assertThat(mAvailableDevicesCategory.getPreferenceCount()).isEqualTo(0); 134 verify(mFragment).startScanning(); 135 136 // Subsequent scan started event will not trigger start/stop nor list clear 137 mFragment.onScanningStateChanged(true); 138 verify(mFragment, times(1)).startScanning(); 139 verify(mAvailableDevicesCategory, times(1)).setProgress(true); 140 141 // Subsequent scan finished event will trigger scan start without list clean 142 mFragment.onScanningStateChanged(false); 143 verify(mFragment, times(2)).startScanning(); 144 verify(mAvailableDevicesCategory, times(2)).setProgress(true); 145 146 // Subsequent scan started event will not trigger any change 147 mFragment.onScanningStateChanged(true); 148 verify(mFragment, times(2)).startScanning(); 149 verify(mAvailableDevicesCategory, times(3)).setProgress(true); 150 verify(mFragment, never()).stopScanning(); 151 152 // Disable scanning will trigger scan stop 153 mFragment.disableScanning(); 154 verify(mFragment, times(1)).stopScanning(); 155 156 // Subsequent scan start event will not trigger any change besides progress circle 157 mFragment.onScanningStateChanged(true); 158 verify(mAvailableDevicesCategory, times(4)).setProgress(true); 159 160 // However, subsequent scan finished event won't trigger new scan start and will stop 161 // progress circle from spinning 162 mFragment.onScanningStateChanged(false); 163 verify(mAvailableDevicesCategory, times(1)).setProgress(false); 164 verify(mFragment, times(2)).startScanning(); 165 verify(mFragment, times(1)).stopScanning(); 166 167 // Verify that clean up only happen once at initialization 168 verify(mAvailableDevicesCategory, times(1)).removeAll(); 169 } 170 }