Files
app_Settings/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
Chaohui Wang bb47f32011 Fix bluetooth settings pairing page stuck
There is heavy work to do when add device to list in the
DeviceListPreferenceFragment, off load these work from main thread
to solve the issue.

Make devicePreferenceMap a ConcurrentHashMap to avoid potential race
condition.

Also no longer use getCachedPreference(key) since we not put anything
into the cache, the fallback flow is always used.

Also in BluetoothDevicePreference.onPreferenceAttributesChanged(), move
more heavy work to background thread.

Using System.currentTimeMillis() to sort devices could cause flaky
because System.currentTimeMillis() could be same for different device,
use AtomicInteger instead.

Fix: 286628533
Test: Following the step in bug
Change-Id: Ia9750adb6b4c1424d084381e9d7c2ca8e7912391
2023-06-15 08:43:17 +00:00

170 lines
6.6 KiB
Java

/*
* Copyright (C) 2017 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.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
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 android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.widget.FooterPreference;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
public class BluetoothPairingDetailTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private final Lifecycle mFakeLifecycle = new Lifecycle() {
@Override
public void addObserver(@NonNull LifecycleObserver observer) {}
@Override
public void removeObserver(@NonNull LifecycleObserver observer) {}
@NonNull
@Override
public State getCurrentState() {
return State.CREATED;
}
};
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private LocalBluetoothManager mLocalManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private CachedBluetoothDeviceManager mDeviceManager;
private BluetoothPairingDetail mFragment;
private BluetoothProgressCategory mAvailableDevicesCategory;
private FooterPreference mFooterPreference;
private BluetoothAdapter mBluetoothAdapter;
@Before
public void setUp() {
mFragment = spy(new BluetoothPairingDetail());
doReturn(mContext).when(mFragment).getContext();
mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
mFooterPreference = new FooterPreference(mContext);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
doReturn(mAvailableDevicesCategory).when(mFragment)
.findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
doReturn(mFooterPreference).when(mFragment)
.findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
doReturn(new View(mContext)).when(mFragment).getView();
doReturn((LifecycleOwner) () -> mFakeLifecycle).when(mFragment).getViewLifecycleOwner();
doReturn(Collections.emptyList()).when(mDeviceManager).getCachedDevicesCopy();
mFragment.mBluetoothAdapter = mBluetoothAdapter;
mFragment.mLocalManager = mLocalManager;
mFragment.mCachedDeviceManager = mDeviceManager;
mFragment.mDeviceListGroup = mAvailableDevicesCategory;
mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
}
@Test
public void initPreferencesFromPreferenceScreen_findPreferences() {
mFragment.initPreferencesFromPreferenceScreen();
assertThat(mFragment.mAvailableDevicesCategory).isEqualTo(mAvailableDevicesCategory);
assertThat(mFragment.mFooterPreference).isEqualTo(mFooterPreference);
}
@Test
public void updateContent_stateOn_addDevices() {
mFragment.initPreferencesFromPreferenceScreen();
mFragment.updateContent(BluetoothAdapter.STATE_ON);
assertThat(mFragment.mAlwaysDiscoverable.mStarted).isEqualTo(true);
assertThat(mBluetoothAdapter.getScanMode())
.isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
}
@Test
public void onScanningStateChanged_restartScanAfterInitialScanning() {
mFragment.initPreferencesFromPreferenceScreen();
// Initial Bluetooth ON will trigger scan enable, list clear and scan start
mFragment.updateContent(BluetoothAdapter.STATE_ON);
verify(mFragment).enableScanning();
assertThat(mAvailableDevicesCategory.getPreferenceCount()).isEqualTo(0);
verify(mFragment).startScanning();
// Subsequent scan started event will not trigger start/stop nor list clear
mFragment.onScanningStateChanged(true);
verify(mFragment, times(1)).startScanning();
verify(mAvailableDevicesCategory, times(1)).setProgress(true);
// Subsequent scan finished event will trigger scan start without list clean
mFragment.onScanningStateChanged(false);
verify(mFragment, times(2)).startScanning();
verify(mAvailableDevicesCategory, times(2)).setProgress(true);
// Subsequent scan started event will not trigger any change
mFragment.onScanningStateChanged(true);
verify(mFragment, times(2)).startScanning();
verify(mAvailableDevicesCategory, times(3)).setProgress(true);
verify(mFragment, never()).stopScanning();
// Disable scanning will trigger scan stop
mFragment.disableScanning();
verify(mFragment, times(1)).stopScanning();
// Subsequent scan start event will not trigger any change besides progress circle
mFragment.onScanningStateChanged(true);
verify(mAvailableDevicesCategory, times(4)).setProgress(true);
// However, subsequent scan finished event won't trigger new scan start and will stop
// progress circle from spinning
mFragment.onScanningStateChanged(false);
verify(mAvailableDevicesCategory, times(1)).setProgress(false);
verify(mFragment, times(2)).startScanning();
verify(mFragment, times(1)).stopScanning();
// Verify that clean up only happen once at initialization
verify(mAvailableDevicesCategory, times(1)).removeAll();
}
}