feat: SettingsLib验证通过-使用App module启用

This commit is contained in:
2024-12-09 16:04:49 +08:00
parent 1f18a59dab
commit a7f5c61005
1562 changed files with 181632 additions and 18 deletions

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class CaptionSettingsPreferenceControllerTest {
private Context mContext;
private PreferenceControllerTestHelper<CaptionSettingsPreferenceController>
mPreferenceControllerHelper;
private Preference mPreference;
@Before
public void setup() {
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
CaptionSettingsPreferenceController.class, mPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
}
@After
public void tearDown() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0);
}
@Test
public void testRefreshUi_captionsDisabled_summarySetToOff() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getString(R.string.captions_settings_off));
}
@Test
public void testRefreshUi_captionsEnabled_summarySetToOn() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 1);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getString(R.string.captions_settings_on));
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.lifecycle.Lifecycle;
import androidx.preference.ListPreference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class CaptionsTextSizeListPreferenceControllerTest {
private Context mContext;
private PreferenceControllerTestHelper<CaptionsTextSizeListPreferenceController>
mPreferenceControllerHelper;
private ListPreference mListPreference;
private String[] mFontSizeTitles;
@Before
public void setup() {
mContext = RuntimeEnvironment.application;
mListPreference = new ListPreference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
CaptionsTextSizeListPreferenceController.class, mListPreference);
mFontSizeTitles = mPreferenceControllerHelper.getController().mFontSizeTitles;
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
}
@After
public void tearDown() {
Settings.Secure.putFloat(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE, 1.0F);
}
@Test
public void testUpdateUi_includesAllTextSizeOptions() {
mPreferenceControllerHelper.getController().refreshUi();
assertThat(Arrays.asList(mListPreference.getEntries())).containsExactlyElementsIn(
mFontSizeTitles);
}
@Test
public void testUpdateUi_selectsCurrentFontSize() {
Settings.Secure.putFloat(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE, 1.5F);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mListPreference.getValue()).isEqualTo("1.5");
assertThat(mListPreference.getSummary()).isEqualTo(mFontSizeTitles[3]);
}
@Test
public void testUpdateUi_noKnownValueSelected_selectsDefaultFontSize() {
Settings.Secure.putFloat(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE, 100.0F);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mListPreference.getValue()).isEqualTo("1.0");
assertThat(mListPreference.getSummary()).isEqualTo(mFontSizeTitles[2]);
}
@Test
public void testFontSizeSelected_updatesFontSizeConstant() {
mListPreference.getOnPreferenceChangeListener().onPreferenceChange(
mListPreference, "1.5");
assertThat(Settings.Secure.getFloat(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE, 1.0F)).isEqualTo(1.5F);
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.lifecycle.Lifecycle;
import androidx.preference.ListPreference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class CaptionsTextStyleListPreferenceControllerTest {
private Context mContext;
private PreferenceControllerTestHelper<CaptionsTextStyleListPreferenceController>
mPreferenceControllerHelper;
private ListPreference mListPreference;
private String[] mFontStyleTitles;
@Before
public void setup() {
mContext = RuntimeEnvironment.application;
mListPreference = new ListPreference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
CaptionsTextStyleListPreferenceController.class, mListPreference);
mFontStyleTitles = mPreferenceControllerHelper.getController().mFontStyleTitles;
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
}
@After
public void tearDown() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, 4);
}
@Test
public void testUpdateUi_includesAllTextStyleOptions() {
mPreferenceControllerHelper.getController().refreshUi();
assertThat(Arrays.asList(mListPreference.getEntries())).containsExactlyElementsIn(
mFontStyleTitles);
}
@Test
public void testUpdateUi_selectsCurrentFontStylePreset() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, 0);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mListPreference.getValue()).isEqualTo("0");
assertThat(mListPreference.getSummary()).isEqualTo(mFontStyleTitles[1]);
}
@Test
public void testUpdateUi_noKnownValueSelected_selectsDefaultFontStylePreset() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, 50);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mListPreference.getValue()).isEqualTo("4");
assertThat(mListPreference.getSummary()).isEqualTo(mFontStyleTitles[0]);
}
@Test
public void testFontSizeSelected_updatesFontSizeConstant() {
mListPreference.getOnPreferenceChangeListener().onPreferenceChange(
mListPreference, "1");
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, 4)).isEqualTo(1);
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.view.accessibility.AccessibilityManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceCategory;
import androidx.test.core.app.ApplicationProvider;
import com.android.car.settings.R;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowAccessibilityManager;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class ScreenReaderCategoryPreferenceControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private PreferenceCategory mPreference;
private PreferenceControllerTestHelper<ScreenReaderCategoryPreferenceController>
mPreferenceControllerHelper;
private ShadowAccessibilityManager mShadowAccessibilityManager;
private ComponentName mScreenReaderComponent;
@Before
public void setUp() {
mPreference = new PreferenceCategory(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
ScreenReaderCategoryPreferenceController.class, mPreference);
mShadowAccessibilityManager = Shadows.shadowOf(
mContext.getSystemService(AccessibilityManager.class));
mScreenReaderComponent = ComponentName.unflattenFromString(
mContext.getString(R.string.config_default_screen_reader));
}
@Test
public void testGetAvailability_screenReaderInstalled_isAvailable() throws Exception {
ResolveInfo resolveInfo = new ResolveInfo();
ServiceInfo serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(accessibilityServiceInfo));
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
AVAILABLE);
}
@Test
public void testGetAvailability_screenReaderInstalled_zoneWrite() throws Exception {
ResolveInfo resolveInfo = new ResolveInfo();
ServiceInfo serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("write");
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
AVAILABLE);
}
@Test
public void testGetAvailability_screenReaderInstalled_zoneRead() throws Exception {
ResolveInfo resolveInfo = new ResolveInfo();
ServiceInfo serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("read");
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
AVAILABLE_FOR_VIEWING);
}
@Test
public void testGetAvailability_screenReaderInstalled_zoneHidden() throws Exception {
ResolveInfo resolveInfo = new ResolveInfo();
ServiceInfo serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("hidden");
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testGetAvailability_screenReaderNotInstalled_isUnavailable() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of());
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testGetAvailability_screenReaderNotInstalled_isUnavailable_zoneWrite() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of());
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("write");
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testGetAvailability_screenReaderNotInstalled_isUnavailable_zoneRead() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of());
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("read");
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testGetAvailability_screenReaderNotInstalled_isUnavailable_zoneHidden() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of());
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("hidden");
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ApplicationProvider;
import com.android.car.settings.R;
import com.android.car.settings.common.ColoredSwitchPreference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.internal.accessibility.util.AccessibilityUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ScreenReaderEnabledSwitchPreferenceControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private ColoredSwitchPreference mColoredSwitchPreference;
private PreferenceControllerTestHelper<ScreenReaderEnabledSwitchPreferenceController>
mPreferenceControllerHelper;
private ComponentName mScreenReaderComponent;
@Before
public void setUp() {
mColoredSwitchPreference = new ColoredSwitchPreference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
ScreenReaderEnabledSwitchPreferenceController.class,
mColoredSwitchPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
mScreenReaderComponent = ComponentName.unflattenFromString(
mContext.getString(R.string.config_default_screen_reader));
}
@Test
public void testRefreshUi_screenReaderEnabled_switchSetToOn() {
setScreenReaderEnabled(true);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mColoredSwitchPreference.isChecked()).isTrue();
}
@Test
public void testRefreshUi_screenReaderDisabled_switchSetToOff() {
setScreenReaderEnabled(false);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mColoredSwitchPreference.isChecked()).isFalse();
}
@Test
public void testSwitchedSetOn_setsScreenReaderEnabled() {
setScreenReaderEnabled(false);
mColoredSwitchPreference.getOnPreferenceChangeListener().onPreferenceChange(
mColoredSwitchPreference, true);
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext,
UserHandle.myUserId())).contains(mScreenReaderComponent);
}
@Test
public void testSwitchedSetOff_setsScreenReaderDisabled() {
setScreenReaderEnabled(true);
mColoredSwitchPreference.getOnPreferenceChangeListener().onPreferenceChange(
mColoredSwitchPreference, false);
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext,
UserHandle.myUserId())).doesNotContain(mScreenReaderComponent);
}
private void setScreenReaderEnabled(boolean enabled) {
AccessibilityUtils.setAccessibilityServiceState(mContext,
mScreenReaderComponent, enabled,
UserHandle.myUserId());
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2022 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.car.settings.accessibility;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.view.accessibility.AccessibilityManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.car.settings.R;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowAccessibilityManager;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@RunWith(RobolectricTestRunner.class)
public class ScreenReaderSettingsIntentPreferenceControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
ShadowAccessibilityManager mShadowAccessibilityManager;
private ComponentName mScreenReaderComponent;
private PreferenceControllerTestHelper<ScreenReaderSettingsIntentPreferenceController>
mPreferenceControllerHelper;
private Preference mPreference;
@Before
public void setUp() {
mPreference = new Preference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
ScreenReaderSettingsIntentPreferenceController.class,
mPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
mScreenReaderComponent = ComponentName.unflattenFromString(
mContext.getString(R.string.config_default_screen_reader));
mShadowAccessibilityManager = Shadows.shadowOf(mContext.getSystemService(
AccessibilityManager.class));
}
@Test
public void testUpdateState_noSettingsActivity_hidesSettings()
throws XmlPullParserException, IOException {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.labelRes = R.string.fake_title;
resolveInfo.nonLocalizedLabel = mContext.getString(R.string.fake_title);
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.applicationInfo = new ApplicationInfo();
serviceInfo.applicationInfo.name = mScreenReaderComponent.getPackageName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
ImmutableList.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testUpdateState_noSettingsActivity_hidesSettings_zoneWrite()
throws XmlPullParserException, IOException {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.labelRes = R.string.fake_title;
resolveInfo.nonLocalizedLabel = mContext.getString(R.string.fake_title);
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.applicationInfo = new ApplicationInfo();
serviceInfo.applicationInfo.name = mScreenReaderComponent.getPackageName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
ImmutableList.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("write");
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testUpdateState_noSettingsActivity_hidesSettings_zoneRead()
throws XmlPullParserException, IOException {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.labelRes = R.string.fake_title;
resolveInfo.nonLocalizedLabel = mContext.getString(R.string.fake_title);
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.applicationInfo = new ApplicationInfo();
serviceInfo.applicationInfo.name = mScreenReaderComponent.getPackageName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
ImmutableList.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("read");
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testUpdateState_noSettingsActivity_hidesSettings_zoneHidden()
throws XmlPullParserException, IOException {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.labelRes = R.string.fake_title;
resolveInfo.nonLocalizedLabel = mContext.getString(R.string.fake_title);
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = mScreenReaderComponent.getPackageName();
serviceInfo.name = mScreenReaderComponent.getClassName();
resolveInfo.serviceInfo = serviceInfo;
serviceInfo.applicationInfo = new ApplicationInfo();
serviceInfo.applicationInfo.name = mScreenReaderComponent.getPackageName();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(
resolveInfo, mContext);
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
ImmutableList.of(accessibilityServiceInfo));
mPreferenceControllerHelper.getController().setAvailabilityStatusForZone("hidden");
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreferenceControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
CONDITIONALLY_UNAVAILABLE);
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.car.settings.R;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.internal.accessibility.util.AccessibilityUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ScreenReaderSettingsPreferenceControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private Preference mPreference;
private PreferenceControllerTestHelper<ScreenReaderSettingsPreferenceController>
mPreferenceControllerHelper;
private ComponentName mScreenReaderComponent;
@Before
public void setUp() {
mPreference = new Preference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
ScreenReaderSettingsPreferenceController.class, mPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
mScreenReaderComponent = ComponentName.unflattenFromString(
mContext.getString(R.string.config_default_screen_reader));
}
@Test
public void testRefreshUi_screenReaderDisabled_summarySetToOff() {
setScreenReaderEnabled(false);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getString(R.string.screen_reader_settings_off));
}
@Test
public void testRefreshUi_screenReaderEnabled_summarySetToOn() {
setScreenReaderEnabled(true);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getString(R.string.screen_reader_settings_on));
}
private void setScreenReaderEnabled(boolean enabled) {
AccessibilityUtils.setAccessibilityServiceState(mContext,
mScreenReaderComponent, enabled,
UserHandle.myUserId());
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2021 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.car.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.lifecycle.Lifecycle;
import com.android.car.settings.common.ColoredSwitchPreference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class ShowCaptionsSwitchPreferenceControllerTest {
private Context mContext;
private PreferenceControllerTestHelper<ShowCaptionsSwitchPreferenceController>
mPreferenceControllerHelper;
private ColoredSwitchPreference mColoredSwitchPreference;
@Before
public void setup() {
mContext = RuntimeEnvironment.application;
mColoredSwitchPreference = new ColoredSwitchPreference(mContext);
mPreferenceControllerHelper =
new PreferenceControllerTestHelper<>(mContext,
ShowCaptionsSwitchPreferenceController.class, mColoredSwitchPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
}
@After
public void tearDown() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0);
}
@Test
public void testRefreshUi_captionsDisabled_switchSetToOff() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mColoredSwitchPreference.isChecked()).isFalse();
}
@Test
public void testRefreshUi_captionsEnabled_switchSetToOn() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 1);
mPreferenceControllerHelper.getController().refreshUi();
assertThat(mColoredSwitchPreference.isChecked()).isTrue();
}
@Test
public void testSwitchedSetOn_setsSystemCaptionSettingsEnabled() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0);
mColoredSwitchPreference.getOnPreferenceChangeListener().onPreferenceChange(
mColoredSwitchPreference, true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0)).isEqualTo(1);
}
@Test
public void testSwitchedSetOff_setsSystemCaptionSettingsDisabled() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 1);
mColoredSwitchPreference.getOnPreferenceChangeListener().onPreferenceChange(
mColoredSwitchPreference, false);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, -1)).isEqualTo(0);
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2018 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.car.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.SwitchPreference;
import com.android.car.settings.common.ConfirmationDialogFragment;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowContentResolver;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
/** Unit tests for {@link AccountAutoSyncPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowContentResolver.class})
public class AccountAutoSyncPreferenceControllerTest {
private final int mUserId = UserHandle.myUserId();
private final UserHandle mUserHandle = new UserHandle(mUserId);
private PreferenceControllerTestHelper<AccountAutoSyncPreferenceController> mHelper;
private SwitchPreference mSwitchPreference;
private AccountAutoSyncPreferenceController mController;
private ConfirmationDialogFragment mDialog;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = RuntimeEnvironment.application;
mSwitchPreference = new SwitchPreference(application);
mHelper = new PreferenceControllerTestHelper<>(application,
AccountAutoSyncPreferenceController.class, mSwitchPreference);
mController = mHelper.getController();
mDialog = new ConfirmationDialogFragment.Builder(context).build();
}
@Test
public void testOnCreate_hasPreviousDialog_dialogListenerSet() {
when(mHelper.getMockFragmentController().findDialogByTag(
ConfirmationDialogFragment.TAG)).thenReturn(mDialog);
mHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
assertThat(mDialog.getConfirmListener()).isNotNull();
}
@Test
public void refreshUi_masterSyncOn_preferenceShouldBeChecked() {
mHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
ContentResolver.setMasterSyncAutomaticallyAsUser(true, mUserId);
mController.refreshUi();
assertThat(mSwitchPreference.isChecked()).isTrue();
}
@Test
public void refreshUi_masterSyncOff_preferenceShouldNotBeChecked() {
ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId);
mHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mController.refreshUi();
assertThat(mSwitchPreference.isChecked()).isFalse();
}
@Test
public void onPreferenceClicked_shouldOpenDialog() {
mSwitchPreference.performClick();
verify(mHelper.getMockFragmentController()).showDialog(
any(ConfirmationDialogFragment.class), eq(ConfirmationDialogFragment.TAG));
}
@Test
public void onConfirm_shouldTogglePreference() {
// Set the preference as unchecked first so that the state is known
mHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId);
mController.refreshUi();
assertThat(mSwitchPreference.isChecked()).isFalse();
Bundle arguments = new Bundle();
arguments.putBoolean(AccountAutoSyncPreferenceController.KEY_ENABLING, true);
arguments.putParcelable(AccountAutoSyncPreferenceController.KEY_USER_HANDLE, mUserHandle);
mController.mConfirmListener.onConfirm(arguments);
assertThat(mSwitchPreference.isChecked()).isTrue();
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2019 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.car.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
import static org.testng.Assert.assertThrows;
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import com.android.car.settings.common.ExtraSettingsLoader;
import com.android.car.settings.common.LogicalPreferenceGroup;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowAccountManager;
import com.android.car.settings.testutils.ShadowApplicationPackageManager;
import com.android.car.settings.testutils.ShadowContentResolver;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Unit test for {@link AccountDetailsSettingController}.
*/
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class,
ShadowApplicationPackageManager.class})
public class AccountDetailsSettingControllerTest {
private static final String ACCOUNT_NAME = "account_name";
private static final String MATCHING_ACCOUNT_TYPE = "account_type";
private static final String ACCOUNT_ACCESS_ID = "account_access_id";
private static final String METADATA_IA_ACCOUNT = "com.android.settings.ia.account";
private static final String NOT_MATCHING_ACCOUNT_TYPE = "com.android.settings.ia.account";
private Context mContext;
private PreferenceGroup mPreference;
@Mock
private ExtraSettingsLoader mExtraSettingsLoader;
private AccountDetailsSettingController mAccountDetailsSettingController;
private Account mAccount = new Account(ACCOUNT_NAME, MATCHING_ACCOUNT_TYPE, ACCOUNT_ACCESS_ID);
private List<Preference> mPreferenceList;
private HashMap<Preference, Bundle> mPreferenceBundleMap;
PreferenceControllerTestHelper<AccountDetailsSettingController> mHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mHelper = new PreferenceControllerTestHelper<>(application,
AccountDetailsSettingController.class);
mAccountDetailsSettingController = mHelper.getController();
mAccountDetailsSettingController.setAccount(mAccount);
mPreferenceList = new ArrayList<>();
mPreferenceBundleMap = new HashMap<>();
mPreference = new LogicalPreferenceGroup(application);
mPreference.setIntent(new Intent());
mHelper.setPreference(mPreference);
}
@Test
public void checkInitialized_accountSetAndUserHandleSet_doesNothing() {
mAccountDetailsSettingController = new PreferenceControllerTestHelper<>(application,
AccountDetailsSettingController.class).getController();
mAccountDetailsSettingController.setAccount(mAccount);
mAccountDetailsSettingController.checkInitialized();
}
@Test
public void checkInitialized_nullAccount_throwsIllegalStateException() {
assertThrows(IllegalStateException.class,
() -> new PreferenceControllerTestHelper<>(mContext,
AccountDetailsSettingController.class,
new LogicalPreferenceGroup(mContext)));
}
@Test
public void addExtraSettings_preferenceEmpty_shouldNotAddAnyPreferences() {
mHelper.markState(Lifecycle.State.STARTED);
setupMockSettingLoaderAndRefreshUI();
assertThat(mPreference.getPreferenceCount()).isEqualTo(0);
}
@Test
public void addExtraSettings_preferenceEmpty_isNotVisible() {
mHelper.markState(Lifecycle.State.STARTED);
setupMockSettingLoaderAndRefreshUI();
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void addExtraSettings_accountTypeNotEqual_shouldNotAddAnyPreferences() {
mHelper.markState(Lifecycle.State.STARTED);
Preference preference = new Preference(mContext);
mPreferenceList.add(preference);
Bundle bundle = new Bundle();
bundle.putString(METADATA_IA_ACCOUNT, NOT_MATCHING_ACCOUNT_TYPE);
mPreferenceBundleMap.put(preference, bundle);
setupMockSettingLoaderAndRefreshUI();
assertThat(mPreference.getPreferenceCount()).isEqualTo(0);
}
@Test
public void addExtraSettings_accountTypeNotEqual_isNotVisible() {
mHelper.markState(Lifecycle.State.STARTED);
Preference preference = new Preference(mContext);
mPreferenceList.add(preference);
Bundle bundle = new Bundle();
bundle.putString(METADATA_IA_ACCOUNT, NOT_MATCHING_ACCOUNT_TYPE);
mPreferenceBundleMap.put(preference, bundle);
setupMockSettingLoaderAndRefreshUI();
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void addExtraSettings_accountTypeEqual_shouldAddPreferences() {
Preference preference = new Preference(mContext);
mPreferenceList.add(preference);
Bundle bundle = new Bundle();
bundle.putString(METADATA_IA_ACCOUNT, MATCHING_ACCOUNT_TYPE);
mPreferenceBundleMap.put(preference, bundle);
setupMockSettingLoaderAndRefreshUI();
mHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
assertThat(mPreference.getPreferenceCount()).isEqualTo(1);
}
@Test
public void addExtraSettings_accountTypeEqual_isVisible() {
Preference preference = new Preference(mContext);
mPreferenceList.add(preference);
Bundle bundle = new Bundle();
bundle.putString(METADATA_IA_ACCOUNT, MATCHING_ACCOUNT_TYPE);
mPreferenceBundleMap.put(preference, bundle);
setupMockSettingLoaderAndRefreshUI();
mHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
assertThat(mPreference.isVisible()).isTrue();
}
private void setupMockSettingLoaderAndRefreshUI() {
when(mExtraSettingsLoader.loadPreferences(any())).thenReturn(mPreferenceBundleMap);
mAccountDetailsSettingController.setExtraSettingsLoader(mExtraSettingsLoader);
mAccountDetailsSettingController.refreshUi();
}
}

View File

@@ -0,0 +1,543 @@
/*
* Copyright (C) 2018 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.car.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.robolectric.RuntimeEnvironment.application;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncStatusInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ProviderInfo;
import android.os.Bundle;
import android.os.UserHandle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.common.LogicalPreferenceGroup;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowAccountManager;
import com.android.car.settings.testutils.ShadowApplicationPackageManager;
import com.android.car.settings.testutils.ShadowContentResolver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Unit test for {@link AccountSyncDetailsPreferenceController}.
*/
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowContentResolver.class, ShadowApplicationPackageManager.class,
ShadowAccountManager.class})
public class AccountSyncDetailsPreferenceControllerTest {
private static final int SYNCABLE = 1;
private static final int NOT_SYNCABLE = 0;
private static final int USER_ID = 3;
private static final int NOT_USER_ID = 5;
private static final String AUTHORITY = "authority";
private static final String ACCOUNT_TYPE = "com.acct1";
private static final String DIFFERENT_ACCOUNT_TYPE = "com.acct2";
private final Account mAccount = new Account("acct1", ACCOUNT_TYPE);
private final UserHandle mUserHandle = new UserHandle(USER_ID);
@Mock
ShadowContentResolver.SyncListener mMockSyncListener;
private Context mContext;
private AccountSyncDetailsPreferenceController mController;
private LogicalPreferenceGroup mPreferenceGroup;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = application;
ShadowContentResolver.setSyncListener(mMockSyncListener);
PreferenceControllerTestHelper<AccountSyncDetailsPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
AccountSyncDetailsPreferenceController.class);
mController = helper.getController();
mController.setAccount(mAccount);
mController.setUserHandle(mUserHandle);
mPreferenceGroup = new LogicalPreferenceGroup(mContext);
helper.setPreference(mPreferenceGroup);
helper.markState(Lifecycle.State.STARTED);
}
@After
public void tearDown() {
ShadowContentResolver.reset();
}
@Test
public void refreshUi_syncAdapterDoesNotHaveSameAccountType_shouldNotBeShown() {
// Adds a sync adapter type that is visible but does not have the right account type.
SyncAdapterType syncAdapterType = new SyncAdapterType(AUTHORITY,
DIFFERENT_ACCOUNT_TYPE, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void refreshUi_syncAdapterIsNotVisible_shouldNotBeShown() {
// Adds a sync adapter type that has the right account type but is not visible.
SyncAdapterType syncAdapterType = new SyncAdapterType(AUTHORITY,
ACCOUNT_TYPE, /* userVisible= */ false, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void refreshUi_syncAdapterIsNotSyncable_shouldNotBeShown() {
// Adds a sync adapter type that has the right account type and is visible.
SyncAdapterType syncAdapterType = new SyncAdapterType(AUTHORITY,
ACCOUNT_TYPE, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
// Sets that the sync adapter to not syncable.
ShadowContentResolver.setIsSyncable(mAccount, AUTHORITY, /* syncable= */ NOT_SYNCABLE);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void refreshUi_syncAdapterDoesNotHaveProviderInfo_shouldNotBeShown() {
// Adds a sync adapter type that has the right account type and is visible.
SyncAdapterType syncAdapterType = new SyncAdapterType(AUTHORITY,
ACCOUNT_TYPE, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
// Sets that the sync adapter to syncable.
ShadowContentResolver.setIsSyncable(mAccount, AUTHORITY, /* syncable= */ SYNCABLE);
// However, no provider info is set for the sync adapter, so it shouldn't be visible.
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void refreshUi_providerInfoDoesNotHaveLabel_shouldNotBeShown() {
// Adds a sync adapter type that has the right account type and is visible.
SyncAdapterType syncAdapterType = new SyncAdapterType(AUTHORITY,
ACCOUNT_TYPE, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
// Sets that the sync adapter to syncable.
ShadowContentResolver.setIsSyncable(mAccount, AUTHORITY, /* syncable= */ SYNCABLE);
// Sets provider info for the sync adapter but it does not have a label.
ProviderInfo info = new ProviderInfo();
info.authority = AUTHORITY;
info.name = "";
ProviderInfo[] providers = {info};
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = AUTHORITY;
packageInfo.providers = providers;
getShadowApplicationManager().addPackage(packageInfo);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void refreshUi_providerLabelShouldBeSet() {
// Adds a sync adapter type that has the right account type and is visible.
SyncAdapterType syncAdapterType = new SyncAdapterType(AUTHORITY,
ACCOUNT_TYPE, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
// Sets that the sync adapter to syncable.
ShadowContentResolver.setIsSyncable(mAccount, AUTHORITY, /* syncable= */ SYNCABLE);
// Sets provider info for the sync adapter with a label.
ProviderInfo info = new ProviderInfo();
info.authority = AUTHORITY;
info.name = "label";
ProviderInfo[] providers = {info};
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = AUTHORITY;
packageInfo.providers = providers;
getShadowApplicationManager().addPackage(packageInfo);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(pref.getTitle()).isEqualTo("label");
}
@Test
public void refreshUi_masterSyncOff_syncDisabled_shouldNotBeChecked() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off master sync and automatic sync for the adapter.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ true, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ false,
USER_ID);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
assertThat(pref.isChecked()).isFalse();
}
@Test
public void refreshUi_masterSyncOn_syncDisabled_shouldBeChecked() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns on master sync and turns off automatic sync for the adapter.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ false, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ false,
USER_ID);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
assertThat(pref.isChecked()).isTrue();
}
@Test
public void refreshUi_masterSyncOff_syncEnabled_shouldBeChecked() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off master sync and turns on automatic sync for the adapter.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ true, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ true,
USER_ID);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
assertThat(pref.isChecked()).isTrue();
}
@Test
public void refreshUi_syncDisabled_summaryShouldBeSet() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off automatic sync for the the sync adapter.
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ false,
mUserHandle.getIdentifier());
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(pref.getSummary()).isEqualTo(mContext.getString(R.string.sync_disabled));
}
@Test
public void refreshUi_syncEnabled_activelySyncing_summaryShouldBeSet() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns on automatic sync for the the sync adapter.
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ true,
mUserHandle.getIdentifier());
// Adds the sync adapter to the list of currently syncing adapters.
SyncInfo syncInfo = new SyncInfo(/* authorityId= */ 0, mAccount, AUTHORITY, /* startTime= */
0);
List<SyncInfo> syncs = new ArrayList<>();
syncs.add(syncInfo);
ShadowContentResolver.setCurrentSyncs(syncs);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(pref.getSummary()).isEqualTo(mContext.getString(R.string.sync_in_progress));
}
@Test
public void refreshUi_syncEnabled_syncHasHappened_summaryShouldBeSet() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns on automatic sync for the the sync adapter.
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ true,
mUserHandle.getIdentifier());
// Sets the sync adapter's last successful sync time.
SyncStatusInfo status = new SyncStatusInfo(0);
status.setLastSuccess(0, 83091);
ShadowContentResolver.setSyncStatus(mAccount, AUTHORITY, status);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
String expectedTimeString = mController.formatSyncDate(new Date(83091));
assertThat(pref.getSummary()).isEqualTo(
mContext.getString(R.string.last_synced, expectedTimeString));
}
@Test
public void refreshUi_activelySyncing_notInitialSync_shouldHaveActiveSyncIcon() {
setUpVisibleSyncAdapters(AUTHORITY);
// Adds the sync adapter to the list of currently syncing adapters.
SyncInfo syncInfo = new SyncInfo(/* authorityId= */ 0, mAccount, AUTHORITY, /* startTime= */
0);
List<SyncInfo> syncs = new ArrayList<>();
syncs.add(syncInfo);
ShadowContentResolver.setCurrentSyncs(syncs);
// Sets the sync adapter's initializing state to false (i.e. it's not performing an
// initial sync).
SyncStatusInfo status = new SyncStatusInfo(0);
status.initialize = false;
ShadowContentResolver.setSyncStatus(mAccount, AUTHORITY, status);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(Shadows.shadowOf(pref.getIcon()).getCreatedFromResId()).isEqualTo(
R.drawable.ic_sync_anim);
}
@Test
public void refreshUi_syncPending_notInitialSync_shouldHaveActiveSyncIcon() {
setUpVisibleSyncAdapters(AUTHORITY);
// Sets the sync adapter's initializing state to false (i.e. it's not performing an
// initial sync).
// Also sets the the sync status to pending
SyncStatusInfo status = new SyncStatusInfo(0);
status.initialize = false;
status.pending = true;
ShadowContentResolver.setSyncStatus(mAccount, AUTHORITY, status);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(Shadows.shadowOf(pref.getIcon()).getCreatedFromResId()).isEqualTo(
R.drawable.ic_sync);
}
@Test
public void refreshUi_syncFailed_shouldHaveProblemSyncIcon() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns on automatic sync for the the sync adapter.
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ true,
mUserHandle.getIdentifier());
// Sets the sync adapter's last failure time and message so it appears to have failed
// previously.
SyncStatusInfo status = new SyncStatusInfo(0);
status.lastFailureTime = 10;
status.lastFailureMesg = "too-many-deletions";
ShadowContentResolver.setSyncStatus(mAccount, AUTHORITY, status);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(Shadows.shadowOf(pref.getIcon()).getCreatedFromResId()).isEqualTo(
R.drawable.ic_sync_problem);
}
@Test
public void refreshUi_noSyncStatus_shouldHaveNoIcon() {
setUpVisibleSyncAdapters(AUTHORITY);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
Preference pref = mPreferenceGroup.getPreference(0);
assertThat(pref.getIcon()).isNull();
assertThat(pref.isIconSpaceReserved()).isTrue();
}
@Test
public void onAccountsUpdate_correctUserId_shouldForceUpdatePreferences() {
setUpVisibleSyncAdapters(AUTHORITY);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
ShadowContentResolver.reset();
mController.onAccountsUpdate(mUserHandle);
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void onAccountsUpdate_incorrectUserId_shouldNotForceUpdatePreferences() {
setUpVisibleSyncAdapters(AUTHORITY);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
ShadowContentResolver.reset();
mController.onAccountsUpdate(new UserHandle(NOT_USER_ID));
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
}
@Test
public void onSyncPreferenceClicked_preferenceUnchecked_shouldSetSyncAutomaticallyOff() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off one time sync and turns on automatic sync for the adapter so the preference is
// checked.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ true, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ true,
USER_ID);
mController.refreshUi();
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
pref.performClick();
assertThat(ContentResolver.getSyncAutomaticallyAsUser(mAccount, AUTHORITY,
USER_ID)).isFalse();
}
@Test
public void onSyncPreferenceClicked_preferenceUnchecked_shouldCancelSync() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off one time sync and turns on automatic sync for the adapter so the preference is
// checked.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ true, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ true,
USER_ID);
mController.refreshUi();
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
pref.performClick();
verify(mMockSyncListener).onSyncCanceled(eq(mAccount), eq(AUTHORITY), eq(USER_ID));
}
@Test
public void onSyncPreferenceClicked_preferenceChecked_shouldSetSyncAutomaticallyOn() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off one time sync and automatic sync for the adapter so the preference is
// unchecked.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ true, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ false,
USER_ID);
mController.refreshUi();
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
pref.performClick();
assertThat(ContentResolver.getSyncAutomaticallyAsUser(mAccount, AUTHORITY,
USER_ID)).isTrue();
}
@Test
public void onSyncPreferenceClicked_preferenceChecked_masterSyncOff_shouldRequestSync() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns off master sync and automatic sync for the adapter so the preference is unchecked.
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ false, USER_ID);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, AUTHORITY, /* sync= */ false,
USER_ID);
mController.refreshUi();
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
// Sets master sync off
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ false, USER_ID);
pref.performClick();
verify(mMockSyncListener).onSyncRequested(eq(mAccount), eq(AUTHORITY), eq(USER_ID),
any(Bundle.class));
}
@Test
public void onSyncPreferenceClicked_oneTimeSyncOn_shouldRequestSync() {
setUpVisibleSyncAdapters(AUTHORITY);
// Turns on one time sync mode
ContentResolver.setMasterSyncAutomaticallyAsUser(/* sync= */ false, USER_ID);
mController.refreshUi();
SyncPreference pref = (SyncPreference) mPreferenceGroup.getPreference(0);
pref.performClick();
verify(mMockSyncListener).onSyncRequested(eq(mAccount), eq(AUTHORITY), eq(USER_ID),
any(Bundle.class));
}
private void setUpVisibleSyncAdapters(String... authorities) {
SyncAdapterType[] syncAdapters = new SyncAdapterType[authorities.length];
for (int i = 0; i < authorities.length; i++) {
String authority = authorities[i];
// Adds a sync adapter type that has the right account type and is visible.
SyncAdapterType syncAdapterType = new SyncAdapterType(authority,
ACCOUNT_TYPE, /* userVisible= */ true, /* supportsUploading= */ true);
syncAdapters[i] = syncAdapterType;
// Sets that the sync adapter is syncable.
ShadowContentResolver.setIsSyncable(mAccount, authority, /* syncable= */ SYNCABLE);
// Sets provider info with a label for the sync adapter.
ProviderInfo info = new ProviderInfo();
info.authority = authority;
info.name = "label";
ProviderInfo[] providers = {info};
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = authority;
packageInfo.providers = providers;
getShadowApplicationManager().addPackage(packageInfo);
}
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
}
private ShadowApplicationPackageManager getShadowApplicationManager() {
return Shadow.extract(mContext.getPackageManager());
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (C) 2018 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.car.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.robolectric.RuntimeEnvironment.application;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.SyncAdapterType;
import android.os.UserHandle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.common.FragmentController;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowContentResolver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
/**
* Unit test for {@link AccountSyncPreferenceController}.
*
* <p>Largely copied from {@link com.android.settings.accounts.AccountSyncPreferenceControllerTest}.
*/
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowContentResolver.class})
public class AccountSyncPreferenceControllerTest {
private static final int SYNCABLE = 1;
private static final int NOT_SYNCABLE = 0;
private final Account mAccount = new Account("acct1", "type1");
private final int mUserId = 3;
private final UserHandle mUserHandle = new UserHandle(mUserId);
private AccountSyncPreferenceController mController;
private FragmentController mMockFragmentController;
private Preference mPreference;
@Before
public void setUp() {
PreferenceControllerTestHelper<AccountSyncPreferenceController> helper =
new PreferenceControllerTestHelper<>(application,
AccountSyncPreferenceController.class);
mController = helper.getController();
mMockFragmentController = helper.getMockFragmentController();
mController.setAccount(mAccount);
mController.setUserHandle(mUserHandle);
mPreference = new Preference(application);
helper.setPreference(mPreference);
helper.markState(Lifecycle.State.STARTED);
}
@After
public void tearDown() {
ShadowContentResolver.reset();
}
@Test
public void refreshUi_notSameAccountType_shouldNotCount() {
// Adds a sync adapter type that has a visible user, is syncable, and syncs automatically
// but does not have the right account type.
SyncAdapterType syncAdapterType = new SyncAdapterType("authority",
/* accountType= */ "type5",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
ContentResolver.setIsSyncable(mAccount, "authority", SYNCABLE);
ContentResolver.setSyncAutomaticallyAsUser(mAccount,
"authority", /* sync= */ true, /* userId= */ mUserId);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_all_off));
}
@Test
public void refreshUi_adapterInvisible_shouldNotCount() {
// Adds a sync adapter type that has the right account type, is syncable, and syncs
// automatically, but doesn't have a visible user
SyncAdapterType syncAdapterType = new SyncAdapterType("authority",
/* accountType= */ "type1",
/* userVisible= */ false,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
ContentResolver.setIsSyncable(mAccount, "authority", SYNCABLE);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority",
/* sync= */ true, /* userId= */ mUserId);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_all_off));
}
@Test
public void refreshUi_notSyncable_shouldNotCount() {
// Adds a sync adapter type that is the right account type and a visible user, but is not
// syncable
SyncAdapterType syncAdapterType = new SyncAdapterType("authority",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
ContentResolver.setIsSyncable(mAccount, "authority", NOT_SYNCABLE);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_all_off));
}
@Test
public void refreshUi_masterAutomaticSyncIgnoredAndAccountSyncDisabled_shouldNotCount() {
// Adds a sync adapter type that is the right account type, has a visible user, and is
// syncable, but has master automatic sync ignored and account-level sync disabled
SyncAdapterType syncAdapterType = new SyncAdapterType("authority",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
ContentResolver.setIsSyncable(mAccount, "authority", SYNCABLE);
ContentResolver.setMasterSyncAutomaticallyAsUser(true, mUserId);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority",
/* sync= */ false, /* userId= */ mUserId);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_all_off));
}
@Test
public void refreshUi_masterAutomaticSyncUsed_shouldCount() {
// Adds a sync adapter type that is the right account type, has a visible user, is
// syncable, and has master-level automatic syncing enabled
SyncAdapterType syncAdapterType = new SyncAdapterType("authority",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
ContentResolver.setIsSyncable(mAccount, "authority", SYNCABLE);
ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_all_on));
}
@Test
public void refreshUi_automaticSyncEnabled_shouldCount() {
// Adds a sync adapter type that is the right account type, has a visible user, is
// syncable, and has account-level automatic syncing enabled
SyncAdapterType syncAdapterType = new SyncAdapterType("authority",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
ContentResolver.setIsSyncable(mAccount, "authority", SYNCABLE);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority",
/* sync= */ true, /* userId= */ mUserId);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_all_on));
}
@Test
public void refreshUi_someEnabled_shouldSetSummary() {
SyncAdapterType syncAdapterType1 = new SyncAdapterType("authority1",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType syncAdapterType2 = new SyncAdapterType("authority2",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType syncAdapterType3 = new SyncAdapterType("authority3",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType syncAdapterType4 = new SyncAdapterType("authority4",
/* accountType= */ "type1",
/* userVisible= */ true,
/* supportsUploading= */ true);
SyncAdapterType[] syncAdapters =
{syncAdapterType1, syncAdapterType2, syncAdapterType3, syncAdapterType4};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
// Enable sync for the first three authorities and disable it for the fourth one
ContentResolver.setIsSyncable(mAccount, "authority1", SYNCABLE);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority1",
/* sync= */ true, /* userId= */ mUserId);
ContentResolver.setIsSyncable(mAccount, "authority2", SYNCABLE);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority2",
/* sync= */ true, /* userId= */ mUserId);
ContentResolver.setIsSyncable(mAccount, "authority3", SYNCABLE);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority3",
/* sync= */ true, /* userId= */ mUserId);
ContentResolver.setSyncAutomaticallyAsUser(mAccount, "authority4",
/* sync= */ false, /* userId= */ mUserId);
mController.refreshUi();
assertThat(mPreference.getSummary())
.isEqualTo(application.getString(R.string.account_sync_summary_some_on, 3, 4));
}
@Test
public void handlePreferenceClicked_shouldLaunchAccountSyncDetailsFragment() {
mController.refreshUi();
mController.handlePreferenceClicked(mPreference);
verify(mMockFragmentController).launchFragment(any(AccountSyncDetailsFragment.class));
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2019 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.car.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.robolectric.RuntimeEnvironment.application;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
import android.content.SyncAdapterType;
import com.android.car.settings.R;
import com.android.car.settings.testutils.ShadowAccountManager;
import com.android.car.settings.testutils.ShadowContentResolver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/** Unit tests for {@link AccountTypesHelper}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowContentResolver.class, ShadowAccountManager.class})
public class AccountTypesHelperTest {
private static final String ACCOUNT_TYPE_1 = "com.acct1";
private static final String ACCOUNT_TYPE_2 = "com.acct2";
private static final String ACCOUNT_TYPE_3 = "com.acct3";
private AccountTypesHelper mHelper;
private AccountManager mAccountManager = AccountManager.get(application);
private int mOnChangeListenerInvocations;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// Add authenticated account types.
addAuthenticator(ACCOUNT_TYPE_1, /* label= */ R.string.account_type1_label);
addAuthenticator(ACCOUNT_TYPE_2, /* label= */ R.string.account_type2_label);
mHelper = new AccountTypesHelper(application);
mHelper.setOnChangeListener(() -> mOnChangeListenerInvocations++);
}
@After
public void tearDown() {
ShadowContentResolver.reset();
}
@Test
public void forceUpdate_authorizedAccountTypesShouldBeSet() {
mHelper.forceUpdate();
assertThat(mHelper.getAuthorizedAccountTypes()).containsExactly(
ACCOUNT_TYPE_1, ACCOUNT_TYPE_2);
assertThat(mOnChangeListenerInvocations).isEqualTo(1);
}
@Test
public void forceUpdate_hasAccountTypeFilter_shouldFilterAccounts() {
// Add a filter that should filter out the second account type (com.acct2).
Set<String> accountTypesFilter = new HashSet<>();
accountTypesFilter.add(ACCOUNT_TYPE_1);
mHelper.setAccountTypesFilter(accountTypesFilter);
mHelper.forceUpdate();
assertThat(mHelper.getAuthorizedAccountTypes()).containsExactly(ACCOUNT_TYPE_1);
assertThat(mOnChangeListenerInvocations).isEqualTo(1);
}
@Test
public void forceUpdate_hasAccountExclusionFilter_shouldFilterAccounts() {
// Add a filter that should filter out the first account type (com.acct1).
Set<String> accountExclusionTypesFilter = new HashSet<>();
accountExclusionTypesFilter.add(ACCOUNT_TYPE_1);
mHelper.setAccountTypesExclusionFilter(accountExclusionTypesFilter);
mHelper.forceUpdate();
assertThat(mHelper.getAuthorizedAccountTypes()).containsExactly(ACCOUNT_TYPE_2);
assertThat(mOnChangeListenerInvocations).isEqualTo(1);
}
@Test
public void forceUpdate_doesNotHaveAuthoritiesInFilter_notAuthorized() {
// Add a sync adapter type for the com.acct1 account type that does not have the same
// authority as the one passed to someAuthority.
SyncAdapterType syncAdapterType = new SyncAdapterType("someAuthority",
ACCOUNT_TYPE_1, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
mHelper.setAuthorities(Collections.singletonList("someOtherAuthority"));
// Force an authenticator refresh so the authorities are refreshed.
mHelper.getAuthenticatorHelper().onReceive(application, null);
mHelper.forceUpdate();
assertThat(mHelper.getAuthorizedAccountTypes()).containsExactly(ACCOUNT_TYPE_2);
assertThat(mOnChangeListenerInvocations).isEqualTo(1);
}
@Test
public void refreshUi_hasAuthoritiesInFilter_notAuthorized() {
// Add a sync adapter type for the com.acct1 account type that has the same authority as
// the one passed to someAuthority.
SyncAdapterType syncAdapterType = new SyncAdapterType("someAuthority",
ACCOUNT_TYPE_1, /* userVisible= */ true, /* supportsUploading= */ true);
SyncAdapterType[] syncAdapters = {syncAdapterType};
ShadowContentResolver.setSyncAdapterTypes(syncAdapters);
mHelper.setAuthorities(Collections.singletonList("someAuthority"));
// Force an authenticator refresh so the authorities are refreshed.
mHelper.getAuthenticatorHelper().onReceive(application, null);
mHelper.forceUpdate();
assertThat(mHelper.getAuthorizedAccountTypes()).containsExactly(
ACCOUNT_TYPE_1, ACCOUNT_TYPE_2);
assertThat(mOnChangeListenerInvocations).isEqualTo(1);
}
@Test
public void onAccountsUpdate_currentUserUpdated_shouldForceUpdate() {
assertThat(mHelper.getAuthorizedAccountTypes().size()).isEqualTo(2);
addAuthenticator(ACCOUNT_TYPE_3, /* label= */ R.string.account_type3_label);
// Trigger an account update via the authenticator helper while listening for account
// updates.
mHelper.listenToAccountUpdates();
mHelper.getAuthenticatorHelper().onReceive(application, null);
mHelper.stopListeningToAccountUpdates();
assertThat(mHelper.getAuthorizedAccountTypes()).containsExactly(
ACCOUNT_TYPE_1, ACCOUNT_TYPE_2, ACCOUNT_TYPE_3);
assertWithMessage("listener should be invoked twice")
.that(mOnChangeListenerInvocations).isEqualTo(2);
}
private void addAuthenticator(String type, int labelRes) {
getShadowAccountManager().addAuthenticator(
new AuthenticatorDescription(type, "com.android.car.settings",
labelRes, 0, 0, 0, false));
}
private ShadowAccountManager getShadowAccountManager() {
return Shadow.extract(mAccountManager);
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2019 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.car.settings.applications;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.storage.VolumeInfo;
import androidx.lifecycle.Lifecycle;
import com.android.car.settings.R;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowLooper;
import java.util.ArrayList;
/** Unit test for {@link ApplicationListItemManager}. */
@RunWith(RobolectricTestRunner.class)
public class ApplicationListItemManagerTest {
private static final String LABEL = "label";
private static final String SIZE_STR = "12.34 MB";
private static final String SOURCE = "source";
private static final int UID = 12;
private static final int MILLISECOND_UPDATE_INTERVAL = 500;
private static final int MILLISECOND_MAX_APP_LOAD_WAIT_INTERVAL = 5000;
private Context mContext;
private ApplicationListItemManager mApplicationListItemManager;
@Mock
private VolumeInfo mVolumeInfo;
@Mock
private Lifecycle mLifecycle;
@Mock
private ApplicationsState mAppState;
@Mock
ApplicationsState.AppFilter mAppFilter;
@Mock
ApplicationListItemManager.AppListItemListener mAppListItemListener1;
@Mock
ApplicationListItemManager.AppListItemListener mAppListItemListener2;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mApplicationListItemManager = new ApplicationListItemManager(mVolumeInfo, mLifecycle,
mAppState, MILLISECOND_UPDATE_INTERVAL, MILLISECOND_MAX_APP_LOAD_WAIT_INTERVAL);
}
@Test
public void startLoading_shouldStartNewSession() {
mApplicationListItemManager.startLoading(mAppFilter, /* param= */ null);
verify(mAppState).newSession(any(), eq(mLifecycle));
}
@Test
public void onRebuildComplete_shouldNotifyRegisteredListener() {
ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = UID;
appInfo.sourceDir = SOURCE;
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo,
/* id= */ 1234L);
appEntry.label = LABEL;
appEntry.sizeStr = SIZE_STR;
appEntry.icon = mContext.getDrawable(R.drawable.test_icon);
apps.add(appEntry);
mApplicationListItemManager.registerListener(mAppListItemListener1);
mApplicationListItemManager.registerListener(mAppListItemListener2);
mApplicationListItemManager.onRebuildComplete(apps);
verify(mAppListItemListener1).onDataLoaded(apps);
verify(mAppListItemListener2).onDataLoaded(apps);
}
@Test
public void onRebuildComplete_unRegisterOneListener_shouldNotifyRegisteredListener() {
ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = UID;
appInfo.sourceDir = SOURCE;
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo,
/* id= */ 1234L);
appEntry.label = LABEL;
appEntry.sizeStr = SIZE_STR;
appEntry.icon = mContext.getDrawable(R.drawable.test_icon);
apps.add(appEntry);
mApplicationListItemManager.registerListener(mAppListItemListener1);
mApplicationListItemManager.registerListener(mAppListItemListener2);
mApplicationListItemManager.unregisterlistener(mAppListItemListener2);
mApplicationListItemManager.onRebuildComplete(apps);
verify(mAppListItemListener1).onDataLoaded(apps);
verify(mAppListItemListener2, times(0)).onDataLoaded(apps);
}
@Test
public void onRebuildComplete_calledAgainImmediately_shouldNotRunSecondCallImmediately() {
ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = UID;
appInfo.sourceDir = SOURCE;
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo,
/* id= */ 1234L);
appEntry.label = LABEL;
appEntry.sizeStr = SIZE_STR;
appEntry.icon = mContext.getDrawable(R.drawable.test_icon);
apps.add(appEntry);
mApplicationListItemManager.registerListener(mAppListItemListener1);
mApplicationListItemManager.onRebuildComplete(apps);
mApplicationListItemManager.onRebuildComplete(apps);
verify(mAppListItemListener1, times(1)).onDataLoaded(apps);
}
@Test
public void onRebuildComplete_calledAgainImmediately_shouldRunSecondCallAfterUpdateInterval() {
ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = UID;
appInfo.sourceDir = SOURCE;
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo,
/* id= */ 1234L);
appEntry.label = LABEL;
appEntry.sizeStr = SIZE_STR;
appEntry.icon = mContext.getDrawable(R.drawable.test_icon);
apps.add(appEntry);
mApplicationListItemManager.registerListener(mAppListItemListener1);
mApplicationListItemManager.onRebuildComplete(apps);
mApplicationListItemManager.onRebuildComplete(apps);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mAppListItemListener1, times(2)).onDataLoaded(apps);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2019 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.car.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.pm.UserInfo;
import com.android.car.settings.profiles.ProfileHelper;
import com.android.car.settings.testutils.ShadowDefaultDialerManager;
import com.android.car.settings.testutils.ShadowSmsApplication;
import com.android.car.settings.testutils.ShadowUserHelper;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Collections;
/** Unit test for {@link ApplicationsUtils}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class,
ShadowUserHelper.class})
public class ApplicationsUtilsTest {
private static final String PACKAGE_NAME = "com.android.car.settings.test";
@After
public void tearDown() {
ShadowDefaultDialerManager.reset();
ShadowSmsApplication.reset();
ShadowUserHelper.reset();
}
@Test
public void isKeepEnabledPackage_defaultDialerApplication_returnsTrue() {
ShadowDefaultDialerManager.setDefaultDialerApplication(PACKAGE_NAME);
assertThat(ApplicationsUtils.isKeepEnabledPackage(RuntimeEnvironment.application,
PACKAGE_NAME)).isTrue();
}
@Test
public void isKeepEnabledPackage_defaultSmsApplication_returnsTrue() {
ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(PACKAGE_NAME, "cls"));
assertThat(ApplicationsUtils.isKeepEnabledPackage(RuntimeEnvironment.application,
PACKAGE_NAME)).isTrue();
}
@Test
public void isKeepEnabledPackage_returnsFalse() {
assertThat(ApplicationsUtils.isKeepEnabledPackage(RuntimeEnvironment.application,
PACKAGE_NAME)).isFalse();
}
@Test
public void isProfileOrDeviceOwner_profileOwner_returnsTrue() {
UserInfo userInfo = new UserInfo();
userInfo.id = 123;
DevicePolicyManager dpm = mock(DevicePolicyManager.class);
ProfileHelper profileHelper = mock(ProfileHelper.class);
ShadowUserHelper.setInstance(profileHelper);
when(profileHelper.getAllProfiles()).thenReturn(Collections.singletonList(userInfo));
when(dpm.getProfileOwnerAsUser(userInfo.id)).thenReturn(
new ComponentName(PACKAGE_NAME, "cls"));
assertThat(ApplicationsUtils.isProfileOrDeviceOwner(PACKAGE_NAME, dpm, profileHelper))
.isTrue();
}
@Test
public void isProfileOrDeviceOwner_deviceOwner_returnsTrue() {
DevicePolicyManager dpm = mock(DevicePolicyManager.class);
when(dpm.isDeviceOwnerAppOnAnyUser(PACKAGE_NAME)).thenReturn(true);
assertThat(ApplicationsUtils.isProfileOrDeviceOwner(PACKAGE_NAME, dpm,
mock(ProfileHelper.class))).isTrue();
}
@Test
public void isProfileOrDeviceOwner_returnsFalse() {
assertThat(ApplicationsUtils.isProfileOrDeviceOwner(PACKAGE_NAME,
mock(DevicePolicyManager.class), mock(ProfileHelper.class))).isFalse();
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2018 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.car.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.content.Intent;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowPermissionControllerManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowPermissionControllerManager.class})
public class PermissionsPreferenceControllerTest {
private static final String PACKAGE_NAME = "Test Package Name";
private Context mContext;
private Preference mPreference;
private PreferenceControllerTestHelper<PermissionsPreferenceController>
mPreferenceControllerHelper;
private PermissionsPreferenceController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreferenceControllerHelper = new PreferenceControllerTestHelper<>(mContext,
PermissionsPreferenceController.class);
mController = mPreferenceControllerHelper.getController();
mPreference = new Preference(mContext);
}
@Test
public void testCheckInitialized_noResolveInfo_throwException() {
assertThrows(IllegalStateException.class,
() -> mPreferenceControllerHelper.setPreference(mPreference));
}
@Test
public void testHandlePreferenceClicked_navigateToNextActivity() {
// Setup so the controller knows about the preference.
mController.setPackageName(PACKAGE_NAME);
mPreferenceControllerHelper.setPreference(mPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
assertThat(mController.handlePreferenceClicked(mPreference)).isTrue();
Intent actual = ShadowApplication.getInstance().getNextStartedActivity();
assertThat(actual.getAction()).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSIONS);
assertThat(actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME)).isEqualTo(PACKAGE_NAME);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 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.car.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.content.pm.PackageInfo;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class VersionPreferenceControllerTest {
private static final String TEST_VERSION_NAME = "9";
private Context mContext;
private Preference mPreference;
private PreferenceControllerTestHelper<VersionPreferenceController> mPreferenceControllerHelper;
private VersionPreferenceController mController;
private PackageInfo mPackageInfo;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreferenceControllerHelper = new PreferenceControllerTestHelper<>(mContext,
VersionPreferenceController.class);
mController = mPreferenceControllerHelper.getController();
mPreference = new Preference(mContext);
mPackageInfo = new PackageInfo();
mPackageInfo.versionName = TEST_VERSION_NAME;
}
@Test
public void testCheckInitialized_noPackageInfo_throwException() {
assertThrows(IllegalStateException.class,
() -> mPreferenceControllerHelper.setPreference(mPreference));
}
@Test
public void testRefreshUi_hasPackageInfo_setTitle() {
mController.setPackageInfo(mPackageInfo);
mPreferenceControllerHelper.setPreference(mPreference);
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mController.refreshUi();
assertThat(mPreference.getTitle()).isEqualTo(
mContext.getString(R.string.application_version_label, TEST_VERSION_NAME));
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2019 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.car.settings.applications.defaultapps;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import com.android.car.settings.common.FragmentController;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.ui.preference.CarUiTwoActionIconPreference;
import com.android.settingslib.applications.DefaultAppInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
@RunWith(RobolectricTestRunner.class)
public class DefaultAppsPickerEntryBasePreferenceControllerTest {
private static final Intent TEST_INTENT = new Intent(Settings.ACTION_SETTINGS);
private static class TestDefaultAppsPickerEntryBasePreferenceController extends
DefaultAppsPickerEntryBasePreferenceController {
private final DefaultAppInfo mDefaultAppInfo;
private Intent mSettingIntent;
TestDefaultAppsPickerEntryBasePreferenceController(Context context,
String preferenceKey, FragmentController fragmentController,
CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
mDefaultAppInfo = mock(DefaultAppInfo.class);
}
@Nullable
@Override
protected Intent getSettingIntent(@Nullable DefaultAppInfo info) {
return mSettingIntent;
}
protected void setSettingIntent(Intent settingIntent) {
mSettingIntent = settingIntent;
}
@Nullable
@Override
protected DefaultAppInfo getCurrentDefaultAppInfo() {
return mDefaultAppInfo;
}
}
private Context mContext;
private CarUiTwoActionIconPreference mButtonPreference;
private PreferenceControllerTestHelper<TestDefaultAppsPickerEntryBasePreferenceController>
mControllerHelper;
private TestDefaultAppsPickerEntryBasePreferenceController mController;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
mButtonPreference = new CarUiTwoActionIconPreference(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
TestDefaultAppsPickerEntryBasePreferenceController.class, mButtonPreference);
mController = mControllerHelper.getController();
}
@Test
public void refreshUi_hasNoSettingIntent_actionButtonIsNotVisible() {
mController.setSettingIntent(null);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mController.refreshUi();
assertThat(mButtonPreference.isSecondaryActionVisible()).isFalse();
}
@Test
public void refreshUi_hasSettingIntentButNoResolvableActivity_actionButtonIsNotVisible() {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = null;
Shadows.shadowOf(mContext.getPackageManager()).addResolveInfoForIntent(
TEST_INTENT, resolveInfo);
mController.setSettingIntent(TEST_INTENT);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mController.refreshUi();
assertThat(mButtonPreference.isSecondaryActionVisible()).isFalse();
}
@Test
public void refreshUi_hasSettingIntentButNoVisibleActivity_actionButtonIsVisible() {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.exported = false;
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activityInfo;
Shadows.shadowOf(mContext.getPackageManager()).addResolveInfoForIntent(
TEST_INTENT, resolveInfo);
mController.setSettingIntent(TEST_INTENT);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mController.refreshUi();
assertThat(mButtonPreference.isSecondaryActionVisible()).isTrue();
}
@Test
public void refreshUi_hasSettingIntent_actionButtonIsVisible() {
mController.setSettingIntent(TEST_INTENT);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mController.refreshUi();
assertThat(mButtonPreference.isSecondaryActionVisible()).isTrue();
}
@Test
public void performButtonClick_launchesIntent() {
// Need to spy context because RuntimeEnvironment.application is not an Activity-based
// context, and so throws RuntimeException when we call startActivityForResult.
doNothing().when(mContext).startActivityForResult(
any(String.class), any(Intent.class), eq(0), isNull());
mController.setSettingIntent(TEST_INTENT);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
mButtonPreference.performSecondaryActionClick();
verify(mContext).startActivityForResult(
"android", TEST_INTENT, 0, null);
}
}

View File

@@ -0,0 +1,279 @@
/*
* Copyright (C) 2019 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.car.settings.applications.defaultapps;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.view.autofill.AutofillManager;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowAutofillServiceInfo;
import com.android.car.settings.testutils.ShadowSecureSettings;
import com.android.car.ui.preference.CarUiTwoActionIconPreference;
import com.android.settingslib.applications.DefaultAppInfo;
import com.google.android.collect.Lists;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPackageManager;
import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSecureSettings.class, ShadowAutofillServiceInfo.class})
public class DefaultAutofillPickerEntryPreferenceControllerTest {
private static final String TEST_PACKAGE = "com.android.car.settings.testutils";
private static final String TEST_CLASS = "BaseTestActivity";
private static final String TEST_OTHER_CLASS = "BaseTestOtherActivity";
private static final String TEST_COMPONENT =
new ComponentName(TEST_PACKAGE, TEST_CLASS).flattenToString();
private static final int TEST_USER_ID = 10;
private Context mContext;
private CarUiTwoActionIconPreference mButtonPreference;
private DefaultAutofillPickerEntryPreferenceController mController;
private PreferenceControllerTestHelper<DefaultAutofillPickerEntryPreferenceController>
mControllerHelper;
@Mock
private AutofillManager mAutofillManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Shadows.shadowOf(RuntimeEnvironment.application).setSystemService(
Context.AUTOFILL_MANAGER_SERVICE, mAutofillManager);
mContext = RuntimeEnvironment.application;
mButtonPreference = new CarUiTwoActionIconPreference(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
DefaultAutofillPickerEntryPreferenceController.class, mButtonPreference);
mController = mControllerHelper.getController();
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE,
"");
}
@After
public void tearDown() {
ShadowAutofillServiceInfo.reset();
ShadowSecureSettings.reset();
}
@Test
public void getAvailabilityStatus_autofillManagerIsNull_unsupportedOnDevice() {
Shadows.shadowOf(RuntimeEnvironment.application).setSystemService(
Context.AUTOFILL_MANAGER_SERVICE, null);
// Reinitialize so that it uses the system service set in this test.
CarUiTwoActionIconPreference preference = new CarUiTwoActionIconPreference(mContext);
PreferenceControllerTestHelper<DefaultAutofillPickerEntryPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
DefaultAutofillPickerEntryPreferenceController.class, preference);
DefaultAutofillPickerEntryPreferenceController controller = helper.getController();
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillManagerIsNull_unsupportedOnDevice_zoneWrite() {
Shadows.shadowOf(RuntimeEnvironment.application).setSystemService(
Context.AUTOFILL_MANAGER_SERVICE, null);
// Reinitialize so that it uses the system service set in this test.
CarUiTwoActionIconPreference preference = new CarUiTwoActionIconPreference(mContext);
PreferenceControllerTestHelper<DefaultAutofillPickerEntryPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
DefaultAutofillPickerEntryPreferenceController.class, preference);
DefaultAutofillPickerEntryPreferenceController controller = helper.getController();
controller.setAvailabilityStatusForZone("write");
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillManagerIsNull_unsupportedOnDevice_zoneRead() {
Shadows.shadowOf(RuntimeEnvironment.application).setSystemService(
Context.AUTOFILL_MANAGER_SERVICE, null);
// Reinitialize so that it uses the system service set in this test.
CarUiTwoActionIconPreference preference = new CarUiTwoActionIconPreference(mContext);
PreferenceControllerTestHelper<DefaultAutofillPickerEntryPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
DefaultAutofillPickerEntryPreferenceController.class, preference);
DefaultAutofillPickerEntryPreferenceController controller = helper.getController();
controller.setAvailabilityStatusForZone("read");
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillManagerIsNull_unsupportedOnDevice_zoneHidden() {
Shadows.shadowOf(RuntimeEnvironment.application).setSystemService(
Context.AUTOFILL_MANAGER_SERVICE, null);
// Reinitialize so that it uses the system service set in this test.
CarUiTwoActionIconPreference preference = new CarUiTwoActionIconPreference(mContext);
PreferenceControllerTestHelper<DefaultAutofillPickerEntryPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
DefaultAutofillPickerEntryPreferenceController.class, preference);
DefaultAutofillPickerEntryPreferenceController controller = helper.getController();
controller.setAvailabilityStatusForZone("hidden");
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillNotSupported_unsupportedOnDevice() {
when(mAutofillManager.isAutofillSupported()).thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillNotSupported_unsupportedOnDevice_zoneWrite() {
when(mAutofillManager.isAutofillSupported()).thenReturn(false);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillNotSupported_unsupportedOnDevice_zoneRead() {
when(mAutofillManager.isAutofillSupported()).thenReturn(false);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillNotSupported_unsupportedOnDevice_zoneHidden() {
when(mAutofillManager.isAutofillSupported()).thenReturn(false);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_autofillSupported_isAvailable() {
when(mAutofillManager.isAutofillSupported()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_autofillSupported_isAvailable_zoneWrite() {
when(mAutofillManager.isAutofillSupported()).thenReturn(true);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_autofillSupported_isAvailable_zoneRead() {
when(mAutofillManager.isAutofillSupported()).thenReturn(true);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_autofillSupported_isAvailable_zoneHidden() {
when(mAutofillManager.isAutofillSupported()).thenReturn(true);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getCurrentDefaultAppInfo_noService_returnsNull() {
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE,
"");
assertThat(mController.getCurrentDefaultAppInfo()).isNull();
}
@Test
public void getCurrentDefaultAppInfo_hasService_returnsDefaultAppInfo() {
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE,
TEST_COMPONENT);
DefaultAppInfo info = mController.getCurrentDefaultAppInfo();
assertThat(info.getKey()).isEqualTo(TEST_COMPONENT);
}
@Test
public void getSettingIntent_nullDefaultAppInfo_returnsNull() {
assertThat(mController.getSettingIntent(null)).isNull();
}
@Test
public void getSettingIntent_noServiceInterface_returnsNull() {
Intent intent = new Intent(AutofillService.SERVICE_INTERFACE);
ShadowPackageManager shadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
shadowPackageManager.addResolveInfoForIntent(intent, Collections.emptyList());
DefaultAppInfo info = new DefaultAppInfo(mContext, mContext.getPackageManager(),
TEST_USER_ID, ComponentName.unflattenFromString(TEST_COMPONENT));
assertThat(mController.getSettingIntent(info)).isNull();
}
@Test
public void getSettingIntent_hasServiceInterface_returnsIntent() {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo.packageName = TEST_PACKAGE;
resolveInfo.serviceInfo.name = TEST_CLASS;
ShadowAutofillServiceInfo.setSettingsActivity(TEST_OTHER_CLASS);
Intent intent = new Intent(AutofillService.SERVICE_INTERFACE);
ShadowPackageManager shadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
shadowPackageManager.addResolveInfoForIntent(intent, Lists.newArrayList(resolveInfo));
DefaultAppInfo info = new DefaultAppInfo(mContext, mContext.getPackageManager(),
TEST_USER_ID, ComponentName.unflattenFromString(TEST_COMPONENT));
Intent result = mController.getSettingIntent(info);
assertThat(result.getAction()).isEqualTo(Intent.ACTION_MAIN);
assertThat(result.getComponent()).isEqualTo(
new ComponentName(TEST_PACKAGE, TEST_OTHER_CLASS));
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2019 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.car.settings.applications.defaultapps;
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import androidx.preference.PreferenceGroup;
import com.android.car.settings.common.LogicalPreferenceGroup;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowSecureSettings;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSecureSettings.class})
public class DefaultAutofillPickerPreferenceControllerTest {
private static final String TEST_PACKAGE_NAME = "com.test.package";
private static final String TEST_SERVICE = "TestService";
private Context mContext;
private PreferenceGroup mPreferenceGroup;
private DefaultAutofillPickerPreferenceController mController;
private PreferenceControllerTestHelper<DefaultAutofillPickerPreferenceController>
mControllerHelper;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreferenceGroup = new LogicalPreferenceGroup(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
DefaultAutofillPickerPreferenceController.class, mPreferenceGroup);
mController = mControllerHelper.getController();
}
@After
public void tearDown() {
ShadowSecureSettings.reset();
}
@Test
public void getCandidates_hasServiceWithoutPermissions_returnsEmptyList() {
ResolveInfo serviceResolveInfo = new ResolveInfo();
serviceResolveInfo.serviceInfo = new ServiceInfo();
serviceResolveInfo.serviceInfo.packageName = TEST_PACKAGE_NAME;
serviceResolveInfo.serviceInfo.name = TEST_SERVICE;
serviceResolveInfo.serviceInfo.permission = "";
getShadowPackageManager().addResolveInfoForIntent(
new Intent(AutofillService.SERVICE_INTERFACE), serviceResolveInfo);
assertThat(mController.getCandidates()).hasSize(0);
}
@Test
public void getCandidates_hasServiceWithBindAutofillServicePermission_returnsService() {
ResolveInfo serviceResolveInfo = new ResolveInfo();
serviceResolveInfo.serviceInfo = new ServiceInfo();
serviceResolveInfo.serviceInfo.packageName = TEST_PACKAGE_NAME;
serviceResolveInfo.serviceInfo.name = TEST_SERVICE;
serviceResolveInfo.serviceInfo.permission = Manifest.permission.BIND_AUTOFILL_SERVICE;
getShadowPackageManager().addResolveInfoForIntent(
new Intent(AutofillService.SERVICE_INTERFACE), serviceResolveInfo);
assertThat(mController.getCandidates()).hasSize(1);
}
@Test
public void getCandidates_hasServiceWithBindAutofillPermission_returnsEmptyList() {
ResolveInfo serviceResolveInfo = new ResolveInfo();
serviceResolveInfo.serviceInfo = new ServiceInfo();
serviceResolveInfo.serviceInfo.packageName = TEST_PACKAGE_NAME;
serviceResolveInfo.serviceInfo.name = TEST_SERVICE;
serviceResolveInfo.serviceInfo.permission = Manifest.permission.BIND_AUTOFILL;
getShadowPackageManager().addResolveInfoForIntent(
new Intent(AutofillService.SERVICE_INTERFACE), serviceResolveInfo);
assertThat(mController.getCandidates()).hasSize(0);
}
@Test
public void getCurrentDefaultKey_secureSettingEmpty_returnsNoneKey() {
// Secure Setting not set should return null.
assertThat(mController.getCurrentDefaultKey()).isEqualTo(
DefaultAppsPickerBasePreferenceController.NONE_PREFERENCE_KEY);
}
@Test
public void getCurrentDefaultKey_secureSettingReturnsInvalidString_returnsNoneKey() {
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE,
"invalid");
assertThat(mController.getCurrentDefaultKey()).isEqualTo(
DefaultAppsPickerBasePreferenceController.NONE_PREFERENCE_KEY);
}
@Test
public void getCurrentDefaultKey_secureSettingReturnsValidString_returnsCorrectKey() {
String key = new ComponentName(TEST_PACKAGE_NAME, TEST_SERVICE).flattenToString();
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE,
key);
assertThat(mController.getCurrentDefaultKey()).isEqualTo(key);
}
@Test
public void setCurrentDefault_setInvalidKey_getCurrentDefaultKeyReturnsNone() {
mController.setCurrentDefault("invalid");
assertThat(mController.getCurrentDefaultKey()).isEqualTo(
DefaultAppsPickerBasePreferenceController.NONE_PREFERENCE_KEY);
}
@Test
public void setCurrentDefault_setValidKey_getCurrentDefaultKeyReturnsKey() {
String key = new ComponentName(TEST_PACKAGE_NAME, TEST_SERVICE).flattenToString();
mController.setCurrentDefault(key);
assertThat(mController.getCurrentDefaultKey()).isEqualTo(key);
}
private ShadowPackageManager getShadowPackageManager() {
return Shadows.shadowOf(mContext.getPackageManager());
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertThrows;
import android.bluetooth.BluetoothAdapter;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.common.FragmentController;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import com.android.car.settings.testutils.ShadowBluetoothPan;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowUserManager;
/** Unit test for {@link BluetoothDevicePreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothPan.class})
public class BluetoothDevicePreferenceControllerTest {
@Mock
private CachedBluetoothDevice mDevice;
private Context mContext;
private PreferenceControllerTestHelper<TestBluetoothDevicePreferenceController>
mControllerHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
// Make sure controller is available.
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
TestBluetoothDevicePreferenceController.class);
mControllerHelper.getController().setCachedDevice(mDevice);
mControllerHelper.setPreference(new Preference(mContext));
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
}
@Test
public void setPreference_deviceNotSet_throwsIllegalStateException() {
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
TestBluetoothDevicePreferenceController.class);
assertThrows(IllegalStateException.class,
() -> mControllerHelper.setPreference(new Preference(mContext)));
}
@Test
public void getAvailabilityStatus_disallowConfigBluetooth_disabledForUser() {
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetooth_disabledForUser_zoneWrite() {
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
mControllerHelper.getController().setAvailabilityStatusForZone("write");
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetooth_disabledForUser_zoneRead() {
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
mControllerHelper.getController().setAvailabilityStatusForZone("read");
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetooth_disabledForUser_zoneHidden() {
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
mControllerHelper.getController().setAvailabilityStatusForZone("hidden");
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void onStart_registersDeviceCallback() {
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
verify(mDevice).registerCallback(any(CachedBluetoothDevice.Callback.class));
}
@Test
public void onStop_unregistersDeviceCallback() {
mControllerHelper.markState(Lifecycle.State.STARTED);
ArgumentCaptor<CachedBluetoothDevice.Callback> callbackCaptor = ArgumentCaptor.forClass(
CachedBluetoothDevice.Callback.class);
verify(mDevice).registerCallback(callbackCaptor.capture());
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
verify(mDevice).unregisterCallback(callbackCaptor.getValue());
}
@Test
public void started_onDeviceAttributesChanged_refreshesUi() {
mControllerHelper.markState(Lifecycle.State.STARTED);
ArgumentCaptor<CachedBluetoothDevice.Callback> callbackCaptor = ArgumentCaptor.forClass(
CachedBluetoothDevice.Callback.class);
verify(mDevice).registerCallback(callbackCaptor.capture());
// onCreate, onStart.
assertThat(mControllerHelper.getController().getUpdateStateCallCount()).isEqualTo(2);
callbackCaptor.getValue().onDeviceAttributesChanged();
// onCreate, onStart, callback.
assertThat(mControllerHelper.getController().getUpdateStateCallCount()).isEqualTo(3);
}
private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(UserManager.get(mContext));
}
/** Concrete impl of {@link BluetoothDevicePreferenceController} for testing. */
private static class TestBluetoothDevicePreferenceController extends
BluetoothDevicePreferenceController<Preference> {
private int mUpdateStateCallCount;
TestBluetoothDevicePreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
}
@Override
protected Class<Preference> getPreferenceType() {
return Preference.class;
}
@Override
protected void updateState(Preference preference) {
mUpdateStateCallCount++;
}
int getUpdateStateCallCount() {
return mUpdateStateCallCount;
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.car.settings.R;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.PanProfile;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class BluetoothDeviceProfilePreferenceTest {
@Mock
private LocalBluetoothProfile mProfile;
@Mock
private CachedBluetoothDevice mCachedDevice;
private BluetoothDevice mDevice;
private Context mContext;
private BluetoothDeviceProfilePreference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:AA:BB");
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mProfile.toString()).thenReturn("key");
when(mProfile.getNameResource(mDevice)).thenReturn(R.string.bt_profile_name);
mPreference = new BluetoothDeviceProfilePreference(mContext, mProfile, mCachedDevice);
}
@Test
public void onConstruction_setsProfileStringAsKey() {
assertThat(mPreference.getKey()).isEqualTo(mProfile.toString());
}
@Test
public void onConstruction_setsProfileNameAsTitle() {
assertThat(mPreference.getTitle()).isEqualTo(mContext.getString(R.string.bt_profile_name));
}
@Test
public void onAttached_registersDeviceCallback() {
mPreference.onAttached();
verify(mCachedDevice).registerCallback(any(CachedBluetoothDevice.Callback.class));
}
@Test
public void onAttached_deviceNotBusy_setsEnabled() {
when(mCachedDevice.isBusy()).thenReturn(false);
mPreference.onAttached();
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void onAttached_deviceBusy_setsNotEnabled() {
when(mCachedDevice.isBusy()).thenReturn(true);
mPreference.onAttached();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void onAttached_preferred_setsChecked() {
when(mProfile.isEnabled(mDevice)).thenReturn(true);
mPreference.onAttached();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void onAttached_notPreferred_setsUnchecked() {
when(mProfile.isEnabled(mDevice)).thenReturn(false);
mPreference.onAttached();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onAttached_panProfile_connected_setsChecked() {
mProfile = mock(PanProfile.class);
when(mProfile.getConnectionStatus(mDevice)).thenReturn(STATE_CONNECTED);
when(mProfile.toString()).thenReturn("key");
when(mProfile.getNameResource(mDevice)).thenReturn(R.string.bt_profile_name);
mPreference = new BluetoothDeviceProfilePreference(mContext, mProfile, mCachedDevice);
mPreference.onAttached();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void onAttached_panProfile_notConnected_setsUnchecked() {
mProfile = mock(PanProfile.class);
when(mProfile.getConnectionStatus(mDevice)).thenReturn(STATE_DISCONNECTED);
when(mProfile.toString()).thenReturn("key");
when(mProfile.getNameResource(mDevice)).thenReturn(R.string.bt_profile_name);
mPreference = new BluetoothDeviceProfilePreference(mContext, mProfile, mCachedDevice);
mPreference.onAttached();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onDeviceAttributesChanged_refreshesUi() {
when(mProfile.isEnabled(mDevice)).thenReturn(false);
when(mCachedDevice.isBusy()).thenReturn(false);
ArgumentCaptor<CachedBluetoothDevice.Callback> callbackCaptor = ArgumentCaptor.forClass(
CachedBluetoothDevice.Callback.class);
mPreference.onAttached();
verify(mCachedDevice).registerCallback(callbackCaptor.capture());
assertThat(mPreference.isEnabled()).isTrue();
assertThat(mPreference.isChecked()).isFalse();
when(mProfile.isEnabled(mDevice)).thenReturn(true);
when(mCachedDevice.isBusy()).thenReturn(true);
callbackCaptor.getValue().onDeviceAttributesChanged();
assertThat(mPreference.isEnabled()).isFalse();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void onDetached_unregistersDeviceCallback() {
ArgumentCaptor<CachedBluetoothDevice.Callback> callbackCaptor = ArgumentCaptor.forClass(
CachedBluetoothDevice.Callback.class);
mPreference.onAttached();
verify(mCachedDevice).registerCallback(callbackCaptor.capture());
mPreference.onDetached();
verify(mCachedDevice).unregisterCallback(callbackCaptor.getValue());
}
}

View File

@@ -0,0 +1,319 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import com.android.car.settings.common.FragmentController;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import com.android.car.settings.testutils.ShadowBluetoothPan;
import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import java.util.Arrays;
import java.util.Collections;
/** Unit test for {@link BluetoothDevicesGroupPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothPan.class})
public class BluetoothDevicesGroupPreferenceControllerTest {
@Mock
private BluetoothDeviceFilter.Filter mFilter;
@Mock
private CachedBluetoothDevice mCachedDevice1;
@Mock
private CachedBluetoothDevice mCachedDevice2;
@Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
private CachedBluetoothDeviceManager mSaveRealCachedDeviceManager;
private LocalBluetoothManager mLocalBluetoothManager;
private BluetoothDevice mDevice1;
private PreferenceGroup mPreferenceGroup;
private TestBluetoothDevicesGroupPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = RuntimeEnvironment.application;
mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, /* onInitCallback= */
null);
mSaveRealCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
mCachedDeviceManager);
mDevice1 = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:AA:BB");
when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
BluetoothDevice device2 = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
"BB:AA:33:22:11:00");
when(mCachedDevice2.getDevice()).thenReturn(device2);
// Make sure controller is available.
Shadows.shadowOf(context.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
mPreferenceGroup = new PreferenceCategory(context);
PreferenceControllerTestHelper<TestBluetoothDevicesGroupPreferenceController>
controllerHelper = new PreferenceControllerTestHelper<>(context,
TestBluetoothDevicesGroupPreferenceController.class);
mController = controllerHelper.getController();
mController.setDeviceFilter(mFilter);
controllerHelper.setPreference(mPreferenceGroup);
controllerHelper.markState(Lifecycle.State.STARTED);
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
mSaveRealCachedDeviceManager);
}
@Test
public void refreshUi_filterMatch_addsToGroup() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
assertThat(devicePreference.getCachedDevice()).isEqualTo(mCachedDevice1);
}
@Test
public void refreshUi_filterMatch_addsToPreferenceMap() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.refreshUi();
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
assertThat(mController.getPreferenceMap()).containsEntry(devicePreference.getCachedDevice(),
devicePreference);
}
@Test
public void refreshUi_filterMismatch_removesFromGroup() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
assertThat(devicePreference.getCachedDevice()).isEqualTo(mCachedDevice1);
when(mFilter.matches(mDevice1)).thenReturn(false);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void refreshUi_filterMismatch_removesFromPreferenceMap() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
assertThat(devicePreference.getCachedDevice()).isEqualTo(mCachedDevice1);
when(mFilter.matches(mDevice1)).thenReturn(false);
mController.refreshUi();
assertThat(mController.getPreferenceMap()).doesNotContainKey(mCachedDevice1);
}
@Test
public void refreshUi_noDevices_hidesGroup() {
mController.refreshUi();
assertThat(mPreferenceGroup.isVisible()).isFalse();
}
@Test
public void refreshUi_devices_showsGroup() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.refreshUi();
assertThat(mPreferenceGroup.isVisible()).isTrue();
}
@Test
public void onBluetoothStateChanged_turningOff_clearsPreferences() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
mController.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_OFF);
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
assertThat(mController.getPreferenceMap()).isEmpty();
}
@Test
public void onDeviceAdded_refreshesUi() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(mDevice1)).thenReturn(true);
mController.onDeviceAdded(mCachedDevice1);
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
assertThat(devicePreference.getCachedDevice()).isEqualTo(mCachedDevice1);
}
@Test
public void onDeviceDeleted_refreshesUi() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Arrays.asList(mCachedDevice1, mCachedDevice2));
when(mFilter.matches(any(BluetoothDevice.class))).thenReturn(true);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice2));
mController.onDeviceDeleted(mCachedDevice1);
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
assertThat(devicePreference.getCachedDevice()).isEqualTo(mCachedDevice2);
}
@Test
public void onDeviceDeleted_lastDevice_hidesGroup() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
when(mFilter.matches(any(BluetoothDevice.class))).thenReturn(true);
mController.refreshUi();
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(Collections.emptyList());
mController.onDeviceDeleted(mCachedDevice1);
assertThat(mPreferenceGroup.isVisible()).isFalse();
}
@Test
public void preferenceClicked_callsOnDeviceClicked() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Arrays.asList(mCachedDevice1, mCachedDevice2));
when(mFilter.matches(any(BluetoothDevice.class))).thenReturn(true);
mController.refreshUi();
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(1);
devicePreference.performClick();
assertThat(mController.getClickedDevice()).isEqualTo(devicePreference.getCachedDevice());
}
@Test
public void preferenceClicked_handled() {
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Arrays.asList(mCachedDevice1, mCachedDevice2));
when(mFilter.matches(any(BluetoothDevice.class))).thenReturn(true);
mController.refreshUi();
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(1);
assertThat(devicePreference.getOnPreferenceClickListener().onPreferenceClick(
devicePreference)).isTrue();
}
private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
}
/** Concrete impl of {@link BluetoothDevicesGroupPreferenceController} for testing. */
private static class TestBluetoothDevicesGroupPreferenceController extends
BluetoothDevicesGroupPreferenceController {
private BluetoothDeviceFilter.Filter mFilter;
private CachedBluetoothDevice mClickedDevice;
TestBluetoothDevicesGroupPreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
}
@Override
protected BluetoothDeviceFilter.Filter getDeviceFilter() {
return mFilter;
}
void setDeviceFilter(BluetoothDeviceFilter.Filter filter) {
mFilter = filter;
}
@Override
protected void onDeviceClicked(CachedBluetoothDevice cachedDevice) {
mClickedDevice = cachedDevice;
}
CachedBluetoothDevice getClickedDevice() {
return mClickedDevice;
}
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2018 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.car.settings.bluetooth;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.os.UserManager.DISALLOW_BLUETOOTH;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.android.car.settings.common.PreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowUserManager;
/** Unit test for {@link BluetoothEntryPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
public class BluetoothEntryPreferenceControllerTest {
private Context mContext;
private BluetoothEntryPreferenceController mController;
private UserHandle mMyUserHandle;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mMyUserHandle = UserHandle.of(UserHandle.myUserId());
mController = new PreferenceControllerTestHelper<>(RuntimeEnvironment.application,
BluetoothEntryPreferenceController.class).getController();
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_available() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_available_zoneWrite() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_available_zoneRead() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_available_zoneHidden() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_disallowBluetooth_disabledForUser() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_disabledForUser_zoneWrite() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_BLUETOOTH, true);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_disabledForUser_zoneRead() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_BLUETOOTH, true);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_bluetoothAvailable_disabledForUser_zoneHidden() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_BLUETOOTH, true);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneWrite() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneRead() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneHidden() {
Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(UserManager.get(mContext));
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import com.android.car.settings.testutils.ShadowBluetoothPan;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowUserManager;
/** Unit test for {@link BluetoothNamePreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothPan.class})
public class BluetoothNamePreferenceControllerTest {
private static final String NAME = "name";
private static final String NAME_UPDATED = "name updated";
private Preference mPreference;
private PreferenceControllerTestHelper<BluetoothNamePreferenceController> mControllerHelper;
private BluetoothNamePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = RuntimeEnvironment.application;
// Make sure controller is available.
Shadows.shadowOf(context.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
mPreference = new Preference(context);
mControllerHelper = new PreferenceControllerTestHelper<>(context,
BluetoothNamePreferenceController.class, mPreference);
mController = mControllerHelper.getController();
mControllerHelper.markState(Lifecycle.State.CREATED);
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
}
@Test
public void refreshUi_setsNameAsSummary() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
mController.refreshUi();
assertThat(mPreference.getSummary()).isEqualTo(NAME);
}
@Test
public void refreshUi_noUserRestrictions_setsSelectable() {
mController.refreshUi();
assertThat(mPreference.isSelectable()).isTrue();
}
@Test
public void refreshUi_userHasConfigRestriction_setsNotSelectable() {
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
mController.refreshUi();
assertThat(mPreference.isSelectable()).isFalse();
}
@Test
public void started_localNameChangedBroadcast_updatesSummary() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
mControllerHelper.markState(Lifecycle.State.STARTED);
assertThat(mPreference.getSummary()).isEqualTo(NAME);
BluetoothAdapter.getDefaultAdapter().setName(NAME_UPDATED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED));
assertThat(mPreference.getSummary()).isEqualTo(NAME_UPDATED);
}
@Test
public void stopped_noUpdateOnLocalNameChangedBroadcast() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
mControllerHelper.markState(Lifecycle.State.STARTED);
assertThat(mPreference.getSummary()).isEqualTo(NAME);
mControllerHelper.markState(Lifecycle.State.CREATED);
BluetoothAdapter.getDefaultAdapter().setName(NAME_UPDATED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED));
assertThat(mPreference.getSummary()).isEqualTo(NAME);
}
@Test
public void preferenceClicked_launchesRenameDialog() {
mControllerHelper.markState(Lifecycle.State.STARTED);
mPreference.performClick();
verify(mControllerHelper.getMockFragmentController()).showDialog(
any(LocalRenameDialogFragment.class), eq(LocalRenameDialogFragment.TAG));
}
@Test
public void preferenceClicked_handled() {
mControllerHelper.markState(Lifecycle.State.STARTED);
assertThat(
mPreference.getOnPreferenceClickListener().onPreferenceClick(mPreference)).isTrue();
}
private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(UserManager.get(RuntimeEnvironment.application));
}
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.os.UserManager.DISALLOW_BLUETOOTH;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.android.car.settings.common.PreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.common.FragmentController;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import com.android.car.settings.testutils.ShadowBluetoothPan;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowUserManager;
import org.robolectric.util.ReflectionHelpers;
/** Unit test for {@link BluetoothPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothPan.class})
public class BluetoothPreferenceControllerTest {
@Mock
private BluetoothEventManager mEventManager;
private BluetoothEventManager mSaveRealEventManager;
private LocalBluetoothManager mLocalBluetoothManager;
private PreferenceControllerTestHelper<TestBluetoothPreferenceController> mControllerHelper;
private TestBluetoothPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = RuntimeEnvironment.application;
mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, /* onInitCallback= */
null);
mSaveRealEventManager = mLocalBluetoothManager.getEventManager();
ReflectionHelpers.setField(mLocalBluetoothManager, "mEventManager", mEventManager);
mControllerHelper = new PreferenceControllerTestHelper<>(context,
TestBluetoothPreferenceController.class, new Preference(context));
mController = mControllerHelper.getController();
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
ReflectionHelpers.setField(mLocalBluetoothManager, "mEventManager", mSaveRealEventManager);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_disabledForUser() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_adapterDisabled_conditionallyUnavailable() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().disable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_OFF);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_adapterDisabled_conditionallyUnavailable_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().disable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_OFF);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_adapterDisabled_conditionallyUnavailable_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().disable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_OFF);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_adapterDisabled_conditionallyUnavailable_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().disable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_OFF);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_available() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_available_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_available_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_available_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void onStart_registersEventListener() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
verify(mEventManager).registerCallback(mController);
}
@Test
public void onStop_unregistersEventListener() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
mControllerHelper.markState(Lifecycle.State.STARTED);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
verify(mEventManager).unregisterCallback(mController);
}
private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(UserManager.get(RuntimeEnvironment.application));
}
/** Concrete impl of {@link BluetoothPreferenceController} for testing. */
private static class TestBluetoothPreferenceController extends
BluetoothPreferenceController<Preference> {
TestBluetoothPreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
}
@Override
protected Class<Preference> getPreferenceType() {
return Preference.class;
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import android.app.AlertDialog;
import android.content.Context;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.android.car.settings.R;
import com.android.car.settings.testutils.BaseTestActivity;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowDialog;
/** Unit test for {@link BluetoothRenameDialogFragment}. */
@RunWith(RobolectricTestRunner.class)
public class BluetoothRenameDialogFragmentTest {
private TestBluetoothRenameDialogFragment mFragment;
private AlertDialog mDialog;
@Before
public void setUp() {
BaseTestActivity activity = Robolectric.setupActivity(BaseTestActivity.class);
mFragment = new TestBluetoothRenameDialogFragment();
mFragment.show(activity.getSupportFragmentManager(), /* tag= */ null);
mDialog = (AlertDialog) ShadowDialog.getLatestDialog();
}
@Test
public void initialTextIsCurrentDeviceName() {
EditText editText = getEditText();
assertThat(editText.getText().toString()).isEqualTo(mFragment.getDeviceName());
}
@Test
public void softInputShown() {
InputMethodManager imm =
(InputMethodManager) RuntimeEnvironment.application.getSystemService(
Context.INPUT_METHOD_SERVICE);
assertThat(Shadows.shadowOf(imm).isSoftInputVisible()).isTrue();
}
@Test
public void noUserInput_positiveButtonDisabled() {
assertThat(mDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled()).isFalse();
}
@Test
public void userInput_positiveButtonEnabled() {
EditText editText = getEditText();
editText.append("1234");
assertThat(mDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled()).isTrue();
}
@Test
public void userInput_emptyName_positiveButtonDisabled() {
EditText editText = getEditText();
editText.setText("");
assertThat(mDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled()).isFalse();
}
@Test
public void nameUpdatedByCode_positiveButtonDisabled() {
EditText editText = getEditText();
editText.append("1234");
mFragment.updateDeviceName();
assertThat(mDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled()).isFalse();
}
@Test
public void editorDoneAction_dismissesDialog() {
EditText editText = getEditText();
editText.onEditorAction(EditorInfo.IME_ACTION_DONE);
assertThat(mDialog.isShowing()).isFalse();
}
@Test
public void editorDoneAction_setsDeviceName() {
EditText editText = getEditText();
String editStr = "1234";
String expectedName = mFragment.getDeviceName() + editStr;
editText.append(editStr);
editText.onEditorAction(EditorInfo.IME_ACTION_DONE);
assertThat(mFragment.getDeviceName()).isEqualTo(expectedName);
}
@Test
public void editorDoneAction_emptyName_doesNotSetDeviceName() {
EditText editText = getEditText();
String expectedName = mFragment.getDeviceName();
String editStr = "";
editText.setText(editStr);
editText.onEditorAction(EditorInfo.IME_ACTION_DONE);
assertThat(mFragment.getDeviceName()).isEqualTo(expectedName);
}
@Test
public void positiveButtonClicked_setsDeviceName() {
EditText editText = getEditText();
String editStr = "1234";
String expectedName = mFragment.getDeviceName() + editStr;
editText.append(editStr);
mDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick();
assertThat(mFragment.getDeviceName()).isEqualTo(expectedName);
}
private EditText getEditText() {
return mDialog.findViewById(R.id.textbox);
}
/** Concrete impl of {@link BluetoothRenameDialogFragment} for testing. */
public static class TestBluetoothRenameDialogFragment extends BluetoothRenameDialogFragment {
private String mSetDeviceNameArg = "Device Name";
@Override
@StringRes
protected int getDialogTitle() {
return R.string.bt_rename_dialog_title;
}
@Nullable
@Override
protected String getDeviceName() {
return mSetDeviceNameArg;
}
@Override
protected void setDeviceName(String deviceName) {
mSetDeviceNameArg = deviceName;
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.bluetooth.BluetoothAdapter.EXTRA_LOCAL_NAME;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static com.google.common.truth.Truth.assertThat;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.DialogInterface;
import android.content.Intent;
import android.widget.EditText;
import com.android.car.settings.R;
import com.android.car.settings.testutils.BaseTestActivity;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowDialog;
/** Unit test for {@link LocalRenameDialogFragment}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalRenameDialogFragmentTest {
private static final String NAME = "name";
private static final String NAME_UPDATED = "name updated";
private LocalRenameDialogFragment mFragment;
@Before
public void setUp() {
mFragment = new LocalRenameDialogFragment();
getShadowBluetoothAdapter().setState(STATE_ON);
BluetoothAdapter.getDefaultAdapter().enable();
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
}
@Test
public void getDeviceName_adapterEnabled_returnsLocalAdapterName() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
assertThat(mFragment.getDeviceName()).isEqualTo(NAME);
}
@Test
public void getDeviceName_adapterDisabled_returnsNull() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
BluetoothAdapter.getDefaultAdapter().disable();
assertThat(mFragment.getDeviceName()).isNull();
}
@Test
public void localNameChangedBroadcast_updatesDeviceName() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
AlertDialog dialog = showDialog(mFragment);
EditText editText = dialog.findViewById(R.id.textbox);
assertThat(editText.getText().toString()).isEqualTo(NAME);
BluetoothAdapter.getDefaultAdapter().setName(NAME_UPDATED);
Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
intent.putExtra(EXTRA_LOCAL_NAME, NAME_UPDATED);
RuntimeEnvironment.application.sendBroadcast(intent);
assertThat(editText.getText().toString()).isEqualTo(NAME_UPDATED);
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).isEnabled()).isFalse();
}
@Test
public void setDeviceName_updatesLocalAdapterName() {
BluetoothAdapter.getDefaultAdapter().setName(NAME);
AlertDialog dialog = showDialog(mFragment);
EditText editText = dialog.findViewById(R.id.textbox);
editText.setText(NAME_UPDATED);
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
assertThat(BluetoothAdapter.getDefaultAdapter().getName()).isEqualTo(NAME_UPDATED);
}
private AlertDialog showDialog(LocalRenameDialogFragment fragment) {
BaseTestActivity activity = Robolectric.setupActivity(BaseTestActivity.class);
fragment.show(activity.getSupportFragmentManager(), /* tag= */ null);
return (AlertDialog) ShadowDialog.getLatestDialog();
}
private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
}
}

View File

@@ -0,0 +1,320 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.os.UserManager.DISALLOW_BLUETOOTH;
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.android.car.settings.common.PreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import com.android.car.settings.testutils.ShadowBluetoothPan;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowUserManager;
import org.robolectric.util.ReflectionHelpers;
/** Unit test for {@link PairNewDevicePreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothPan.class})
public class PairNewDevicePreferenceControllerTest {
@Mock
private BluetoothEventManager mEventManager;
private BluetoothEventManager mSaveRealEventManager;
private LocalBluetoothManager mLocalBluetoothManager;
private Context mContext;
private Preference mPreference;
private PreferenceControllerTestHelper<PairNewDevicePreferenceController> mControllerHelper;
private PairNewDevicePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mLocalBluetoothManager = LocalBluetoothManager.getInstance(mContext, /* onInitCallback= */
null);
mSaveRealEventManager = mLocalBluetoothManager.getEventManager();
ReflectionHelpers.setField(mLocalBluetoothManager, "mEventManager", mEventManager);
// Default to available.
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
mPreference = new Preference(mContext);
mPreference.setIntent(new Intent());
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
PairNewDevicePreferenceController.class, mPreference);
mController = mControllerHelper.getController();
mControllerHelper.markState(Lifecycle.State.STARTED);
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
ReflectionHelpers.setField(mLocalBluetoothManager, "mEventManager", mSaveRealEventManager);
}
@Test
public void checkInitialized_noFragmentOrIntent_throwsIllegalStateException() {
assertThrows(IllegalStateException.class,
() -> new PreferenceControllerTestHelper<>(mContext,
PairNewDevicePreferenceController.class, new Preference(mContext)));
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_bluetoothNotAvailable_unsupportedOnDevice_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_disabledForUser() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowBluetoothUserRestriction_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetoothUserRestriction_disabledForUser() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetoothUserRestriction_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetoothUserRestriction_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_disallowConfigBluetoothUserRestriction_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
getShadowUserManager().setUserRestriction(
UserHandle.of(UserHandle.myUserId()), DISALLOW_CONFIG_BLUETOOTH, true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_available() {
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_available_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_available_zoneRead() {
mController.setAvailabilityStatusForZone("read");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_available_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
// No user restrictions.
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void refreshUi_bluetoothAdapterEnabled_setsEmptySummary() {
BluetoothAdapter.getDefaultAdapter().enable();
mController.refreshUi();
assertThat(mPreference.getSummary().toString()).isEmpty();
}
@Test
public void refreshUi_bluetoothAdapterDisabled_setsTurnOnToPairSummary() {
BluetoothAdapter.getDefaultAdapter().disable();
mController.refreshUi();
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getString(R.string.bluetooth_pair_new_device_summary));
}
@Test
public void bluetoothAdapterStateChangedBroadcast_refreshesUi() {
BluetoothAdapter.getDefaultAdapter().enable();
mController.refreshUi();
assertThat(mPreference.getSummary().toString()).isEmpty();
BluetoothAdapter.getDefaultAdapter().disable();
mContext.sendBroadcast(new Intent(BluetoothAdapter.ACTION_STATE_CHANGED));
assertThat(mPreference.getSummary().toString()).isNotEmpty();
}
@Test
public void preferenceClicked_enablesAdapter() {
BluetoothAdapter.getDefaultAdapter().disable();
mPreference.performClick();
assertThat(BluetoothAdapter.getDefaultAdapter().isEnabled()).isTrue();
}
@Test
public void preferenceClicked_notHandled() {
assertThat(mPreference.getOnPreferenceClickListener().onPreferenceClick(
mPreference)).isFalse();
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(UserManager.get(RuntimeEnvironment.application));
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.DialogInterface;
import android.widget.EditText;
import com.android.car.settings.R;
import com.android.car.settings.testutils.BaseTestActivity;
import com.android.car.settings.testutils.ShadowBluetoothAdapter;
import com.android.car.settings.testutils.ShadowBluetoothPan;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.util.ReflectionHelpers;
/** Unit test for {@link RemoteRenameDialogFragment}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothPan.class})
public class RemoteRenameDialogFragmentTest {
private static final String NAME = "name";
private static final String NAME_UPDATED = "name updated";
@Mock
private CachedBluetoothDevice mCachedDevice;
@Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
private CachedBluetoothDeviceManager mSaveRealCachedDeviceManager;
private LocalBluetoothManager mLocalBluetoothManager;
private RemoteRenameDialogFragment mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mLocalBluetoothManager = LocalBluetoothManager.getInstance(
RuntimeEnvironment.application, /* onInitCallback= */ null);
mSaveRealCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
mCachedDeviceManager);
String address = "00:11:22:33:AA:BB";
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
when(mCachedDeviceManager.findDevice(device)).thenReturn(mCachedDevice);
when(mCachedDevice.getAddress()).thenReturn(address);
mFragment = RemoteRenameDialogFragment.newInstance(mCachedDevice);
}
@After
public void tearDown() {
ShadowBluetoothAdapter.reset();
ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
mSaveRealCachedDeviceManager);
}
@Test
public void getDeviceName_returnsCachedDeviceName() {
when(mCachedDevice.getName()).thenReturn(NAME);
showDialog(mFragment); // Attach the fragment.
assertThat(mFragment.getDeviceName()).isEqualTo(NAME);
}
@Test
public void setDeviceName_updatesCachedDeviceName() {
when(mCachedDevice.getName()).thenReturn(NAME);
AlertDialog dialog = showDialog(mFragment);
EditText editText = dialog.findViewById(R.id.textbox);
editText.setText(NAME_UPDATED);
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
verify(mCachedDevice).setName(NAME_UPDATED);
}
private AlertDialog showDialog(RemoteRenameDialogFragment fragment) {
BaseTestActivity activity = Robolectric.setupActivity(BaseTestActivity.class);
fragment.show(activity.getSupportFragmentManager(), /* tag= */ null);
return (AlertDialog) ShadowDialog.getLatestDialog();
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2018 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.car.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import android.text.InputFilter;
import android.text.SpannableStringBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Unit test for {@link Utf8ByteLengthFilter}. */
@RunWith(RobolectricTestRunner.class)
public class Utf8ByteLengthFilterTest {
@Test
public void filter_belowMaxBytes_returnsNull() {
CharSequence source = "1"; // 1 byte.
SpannableStringBuilder dest = new SpannableStringBuilder("abcdefgh"); // 8 bytes.
InputFilter lengthFilter = new Utf8ByteLengthFilter(10);
// Append source to dest.
CharSequence filtered = lengthFilter.filter(source, /* start= */ 0, source.length(), dest,
dest.length(), dest.length());
// Source is not filtered.
assertThat(filtered).isNull();
}
@Test
public void filter_maxBytes_returnsNull() {
CharSequence source = "1"; // 1 byte.
SpannableStringBuilder dest = new SpannableStringBuilder("abcdefghi"); // 9 bytes.
InputFilter lengthFilter = new Utf8ByteLengthFilter(10);
// Append source to dest.
CharSequence filtered = lengthFilter.filter(source, /* start= */ 0, source.length(), dest,
dest.length(), dest.length());
// Source is not filtered.
assertThat(filtered).isNull();
}
@Test
public void filter_aboveMaxBytes_returnsFilteredSource() {
CharSequence source = "12"; // 2 bytes.
SpannableStringBuilder dest = new SpannableStringBuilder("abcdefghi"); // 8 bytes.
InputFilter lengthFilter = new Utf8ByteLengthFilter(10);
// Append source to dest.
CharSequence filtered = lengthFilter.filter(source, /* start= */ 0, source.length(), dest,
dest.length(), dest.length());
// Source is filtered.
assertThat(filtered).isEqualTo("1");
}
// Borrowed from com.android.settings.bluetooth.Utf8ByteLengthFilterTest.
@Test
public void exerciseFilter() {
CharSequence source;
SpannableStringBuilder dest;
InputFilter lengthFilter = new Utf8ByteLengthFilter(10);
InputFilter[] filters = {lengthFilter};
source = "abc";
dest = new SpannableStringBuilder("abcdefgh");
dest.setFilters(filters);
dest.insert(1, source);
String expectedString1 = "aabbcdefgh";
assertThat(dest.toString()).isEqualTo(expectedString1);
dest.replace(5, 8, source);
String expectedString2 = "aabbcabcgh";
assertThat(dest.toString()).isEqualTo(expectedString2);
dest.insert(2, source);
assertThat(dest.toString()).isEqualTo(expectedString2);
dest.delete(1, 3);
String expectedString3 = "abcabcgh";
assertThat(dest.toString()).isEqualTo(expectedString3);
dest.append("12345");
String expectedString4 = "abcabcgh12";
assertThat(dest.toString()).isEqualTo(expectedString4);
source = "\u60a8\u597d"; // 2 Chinese chars == 6 bytes in UTF-8
dest.replace(8, 10, source);
assertThat(dest.toString()).isEqualTo(expectedString3);
dest.replace(0, 1, source);
String expectedString5 = "\u60a8bcabcgh";
assertThat(dest.toString()).isEqualTo(expectedString5);
dest.replace(0, 4, source);
String expectedString6 = "\u60a8\u597dbcgh";
assertThat(dest.toString()).isEqualTo(expectedString6);
source = "\u00a3\u00a5"; // 2 Latin-1 chars == 4 bytes in UTF-8
dest.delete(2, 6);
dest.insert(0, source);
String expectedString7 = "\u00a3\u00a5\u60a8\u597d";
assertThat(dest.toString()).isEqualTo(expectedString7);
dest.replace(2, 3, source);
String expectedString8 = "\u00a3\u00a5\u00a3\u597d";
assertThat(dest.toString()).isEqualTo(expectedString8);
dest.replace(3, 4, source);
String expectedString9 = "\u00a3\u00a5\u00a3\u00a3\u00a5";
assertThat(dest.toString()).isEqualTo(expectedString9);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2018 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.RuntimeEnvironment.application;
import androidx.fragment.app.Fragment;
import com.android.car.settings.R;
import com.android.car.settings.testutils.BaseTestActivity;
import com.android.car.settings.testutils.DialogTestUtils;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
/**
* Tests for ErrorDialog.
*/
@RunWith(RobolectricTestRunner.class)
public class ErrorDialogTest {
private static final String ERROR_DIALOG_TAG = "ErrorDialogTag";
private BaseTestActivity mTestActivity;
private Fragment mTestFragment;
@Before
public void setUpTestActivity() {
MockitoAnnotations.initMocks(this);
mTestActivity = Robolectric.setupActivity(BaseTestActivity.class);
mTestFragment = new Fragment();
mTestActivity.launchFragment(mTestFragment);
}
@Test
public void testOkDismissesDialog() {
ErrorDialog dialog = ErrorDialog.show(mTestFragment, R.string.delete_user_error_title);
assertThat(isDialogShown()).isTrue(); // Dialog is shown.
// Invoke cancel.
DialogTestUtils.clickPositiveButton(dialog);
assertThat(isDialogShown()).isFalse(); // Dialog is dismissed.
}
@Test
@Ignore("b/148687802): Figure out why title returns empty string.")
public void testErrorDialogSetsTitle() {
int testTitleId = R.string.add_user_error_title;
ErrorDialog dialog = ErrorDialog.show(mTestFragment, testTitleId);
assertThat(DialogTestUtils.getTitle(dialog)).isEqualTo(application.getString(testTitleId));
}
private boolean isDialogShown() {
return mTestActivity.getSupportFragmentManager()
.findFragmentByTag(ERROR_DIALOG_TAG) != null;
}
}

View File

@@ -0,0 +1,359 @@
/*
* Copyright (C) 2019 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.car.settings.common;
import static com.android.settingslib.drawer.CategoryKey.CATEGORY_DEVICE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.testutils.ShadowApplicationPackageManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.Map;
/** Unit test for {@link ExtraSettingsLoader}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowApplicationPackageManager.class})
public class ExtraSettingsLoaderTest {
private Context mContext;
private ExtraSettingsLoader mExtraSettingsLoader;
private static final String META_DATA_PREFERENCE_CATEGORY = "com.android.settings.category";
private static final String FAKE_CATEGORY = "fake_category";
private static final String FAKE_TITLE = "fake_title";
private static final String FAKE_SUMMARY = "fake_summary";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
ShadowApplicationPackageManager.setResources(mContext.getResources());
mExtraSettingsLoader = new ExtraSettingsLoader(mContext);
}
@After
public void tearDown() {
ShadowApplicationPackageManager.reset();
}
@Test
public void testLoadPreference_stringResources_shouldLoadResources() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_SUMMARY, FAKE_SUMMARY);
bundle.putString(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).hasSize(1);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isEqualTo(FAKE_SUMMARY);
}
}
@Test
public void testLoadPreference_metadataBundleIsValue() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_SUMMARY, FAKE_SUMMARY);
bundle.putString(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
ResolveInfo resolveInfoNonSystem = new ResolveInfo();
resolveInfoNonSystem.system = false;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoNonSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).hasSize(1);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isEqualTo(FAKE_SUMMARY);
Bundle b = preferenceToBundleMap.get(p);
assertThat(b.getString(META_DATA_PREFERENCE_TITLE)).isEqualTo(FAKE_TITLE);
assertThat(b.getString(META_DATA_PREFERENCE_SUMMARY)).isEqualTo(FAKE_SUMMARY);
assertThat(b.getString(META_DATA_PREFERENCE_CATEGORY)).isEqualTo(FAKE_CATEGORY);
assertThat(b.getInt(META_DATA_PREFERENCE_ICON)).isNotNull();
}
}
@Test
public void testLoadPreference_integerResources_shouldLoadResources() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putInt(META_DATA_PREFERENCE_TITLE, R.string.fake_title);
bundle.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.fake_summary);
bundle.putInt(META_DATA_PREFERENCE_CATEGORY, R.string.fake_category);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
ResolveInfo resolveInfoNonSystem = new ResolveInfo();
resolveInfoNonSystem.system = false;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoNonSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).hasSize(1);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isEqualTo(FAKE_SUMMARY);
assertThat(p.getIcon()).isNotNull();
}
}
@Test
public void testLoadPreference_noDefaultSummary() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isNull();
}
}
@Test
public void testLoadPreference_noCategory_shouldSetToDeviceCategory() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, CATEGORY_DEVICE);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_SUMMARY, FAKE_SUMMARY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).hasSize(1);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isEqualTo(FAKE_SUMMARY);
}
}
@Test
public void testLoadPreference_noCategoryMatched_shouldNotReturnPreferences() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_SUMMARY, FAKE_SUMMARY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).isEmpty();
}
@Test
public void testLoadPreference_shouldLoadDefaultIcon() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_SUMMARY, FAKE_SUMMARY);
bundle.putString(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem = new ResolveInfo();
resolveInfoSystem.system = true;
resolveInfoSystem.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isEqualTo(FAKE_SUMMARY);
assertThat(p.getIcon()).isNotNull();
}
}
@Test
public void testLoadPreference_noSystemApp_returnsNoPreferences() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
ResolveInfo resolveInfoNonSystem1 = new ResolveInfo();
resolveInfoNonSystem1.system = false;
resolveInfoNonSystem1.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoNonSystem1);
ResolveInfo resolveInfoNonSystem2 = new ResolveInfo();
resolveInfoNonSystem2.system = false;
resolveInfoNonSystem2.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoNonSystem2);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).isEmpty();
}
@Test
public void testLoadPreference_systemApp_returnsPreferences() {
Intent intent = new Intent();
intent.putExtra(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
Bundle bundle = new Bundle();
bundle.putString(META_DATA_PREFERENCE_TITLE, FAKE_TITLE);
bundle.putString(META_DATA_PREFERENCE_SUMMARY, FAKE_SUMMARY);
bundle.putString(META_DATA_PREFERENCE_CATEGORY, FAKE_CATEGORY);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.metaData = bundle;
activityInfo.packageName = "package_name";
activityInfo.name = "class_name";
ResolveInfo resolveInfoSystem1 = new ResolveInfo();
resolveInfoSystem1.system = true;
resolveInfoSystem1.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem1);
ResolveInfo resolveInfoNonSystem1 = new ResolveInfo();
resolveInfoNonSystem1.system = false;
resolveInfoNonSystem1.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoNonSystem1);
ResolveInfo resolveInfoSystem2 = new ResolveInfo();
resolveInfoSystem2.system = true;
resolveInfoSystem2.activityInfo = activityInfo;
getShadowPackageManager().addResolveInfoForIntent(intent, resolveInfoSystem2);
Map<Preference, Bundle> preferenceToBundleMap = mExtraSettingsLoader.loadPreferences(
intent);
assertThat(preferenceToBundleMap).hasSize(2);
for (Preference p : preferenceToBundleMap.keySet()) {
assertThat(p.getTitle()).isEqualTo(FAKE_TITLE);
assertThat(p.getSummary()).isEqualTo(FAKE_SUMMARY);
}
}
private ShadowApplicationPackageManager getShadowPackageManager() {
return Shadow.extract(mContext.getPackageManager());
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (C) 2019 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import com.android.car.settings.testutils.ShadowApplicationPackageManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.HashMap;
import java.util.Map;
/** Unit test for {@link ExtraSettingsPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowApplicationPackageManager.class})
public class ExtraSettingsPreferenceControllerTest {
private static final Intent FAKE_INTENT = new Intent();
private Context mContext;
private PreferenceGroup mPreferenceGroup;
private ExtraSettingsPreferenceController mController;
private PreferenceControllerTestHelper<ExtraSettingsPreferenceController>
mPreferenceControllerHelper;
private Map<Preference, Bundle> mPreferenceBundleMapEmpty = new HashMap<>();
private Map<Preference, Bundle> mPreferenceBundleMap = new HashMap<>();
private static final CarUxRestrictions UNRESTRICTED_UX_RESTRICTIONS =
new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
private Bundle mBundle;
@Mock
private ExtraSettingsLoader mExtraSettingsLoaderMock;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreferenceGroup = new LogicalPreferenceGroup(mContext);
mPreferenceGroup.setIntent(FAKE_INTENT);
mPreferenceControllerHelper = new PreferenceControllerTestHelper<>(mContext,
ExtraSettingsPreferenceController.class, mPreferenceGroup);
mController = mPreferenceControllerHelper.getController();
Preference preference = new Preference(mContext);
mBundle = new Bundle();
mPreferenceBundleMap = new HashMap<>();
mPreferenceBundleMap.put(preference, mBundle);
}
@After
public void tearDown() {
ShadowApplicationPackageManager.reset();
}
@Test
public void testRefreshUi_notInitializedYet() {
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
}
@Test
public void testRefreshUi_initialized_noPreferenceAdded() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMapEmpty);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
assertThat(mPreferenceGroup.isVisible()).isEqualTo(false);
}
@Test
public void testRefreshUi_noPreferenceAdded_shouldNotBeVisible() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMapEmpty);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
assertThat(mPreferenceGroup.isVisible()).isEqualTo(false);
}
@Test
public void testRefreshUi_initialized_preferenceAdded() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
}
@Test
public void testRefreshUi_preferenceAdded_shouldBeVisible() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
assertThat(mPreferenceGroup.isVisible()).isEqualTo(true);
}
@Test
public void testRefreshUi_refreshedTwice_shouldOnlyAddPreferenceOnce() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
}
@Test
public void testRefreshUi_refreshedTwice_stillBeVisible() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
mController.refreshUi();
assertThat(mPreferenceGroup.isVisible()).isEqualTo(true);
}
@Test
public void onUxRestrictionsChanged_unrestrictedAndDO_intentsIntoActivityNoMetadata_disabled() {
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
mController.onApplyUxRestrictions(UNRESTRICTED_UX_RESTRICTIONS);
assertThat(mPreferenceGroup.getPreference(0).isEnabled()).isFalse();
}
@Test
public void onUxRestrictionsChanged_unrestrictedAndDO_intentsIntoNonDOActivity_disabled() {
mBundle.putBoolean(
ExtraSettingsPreferenceController.META_DATA_DISTRACTION_OPTIMIZED, false);
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
mController.onApplyUxRestrictions(UNRESTRICTED_UX_RESTRICTIONS);
assertThat(mPreferenceGroup.getPreference(0).isEnabled()).isFalse();
}
@Test
public void onUxRestrictionsChanged_unrestrictedAndDO_intentsIntoDOActivity_enabled() {
mBundle.putBoolean(
ExtraSettingsPreferenceController.META_DATA_DISTRACTION_OPTIMIZED, true);
when(mExtraSettingsLoaderMock.loadPreferences(FAKE_INTENT)).thenReturn(
mPreferenceBundleMap);
mController.setExtraSettingsLoader(mExtraSettingsLoaderMock);
mPreferenceControllerHelper.markState(Lifecycle.State.CREATED);
mController.refreshUi();
mController.onApplyUxRestrictions(UNRESTRICTED_UX_RESTRICTIONS);
assertThat(mPreferenceGroup.getPreference(0).isEnabled()).isTrue();
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2018 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.car.settings.common;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import androidx.preference.Preference;
import java.util.HashSet;
import java.util.Set;
/**
* Concrete {@link PreferenceController} with methods for verifying behavior in tests.
*/
public class FakePreferenceController extends PreferenceController<Preference> {
@AvailabilityStatus
private int mAvailabilityStatus;
private int mCheckInitializedCallCount;
private int mOnCreateInternalCallCount;
private int mOnStartInternalCallCount;
private int mOnResumeInternalCallCount;
private int mOnPauseInternalCallCount;
private int mOnStopInternalCallCount;
private int mOnDestroyInternalCallCount;
private int mUpdateStateCallCount;
private Preference mUpdateStateArg;
private int mHandlePreferenceChangedCallCount;
private Preference mHandlePreferenceChangedPreferenceArg;
private Object mHandlePreferenceChangedValueArg;
private int mHandlePreferenceClickedCallCount;
private Preference mHandlePreferenceClickedArg;
private boolean mAllIgnoresUxRestrictions = false;
private Set<String> mPreferencesIgnoringUxRestrictions = new HashSet<>();
public FakePreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
mAvailabilityStatus = super.getAvailabilityStatus();
}
@Override
protected Class<Preference> getPreferenceType() {
return Preference.class;
}
@Override
protected void checkInitialized() {
mCheckInitializedCallCount++;
}
int getCheckInitializedCallCount() {
return mCheckInitializedCallCount;
}
@Override
@AvailabilityStatus
protected int getDefaultAvailabilityStatus() {
return mAvailabilityStatus;
}
void setAvailabilityStatus(@AvailabilityStatus int availabilityStatus) {
mAvailabilityStatus = availabilityStatus;
}
@Override
protected void onCreateInternal() {
mOnCreateInternalCallCount++;
}
int getOnCreateInternalCallCount() {
return mOnCreateInternalCallCount;
}
@Override
protected void onStartInternal() {
mOnStartInternalCallCount++;
}
int getOnStartInternalCallCount() {
return mOnStartInternalCallCount;
}
@Override
protected void onResumeInternal() {
mOnResumeInternalCallCount++;
}
int getOnResumeInternalCallCount() {
return mOnResumeInternalCallCount;
}
@Override
protected void onPauseInternal() {
mOnPauseInternalCallCount++;
}
int getOnPauseInternalCallCount() {
return mOnPauseInternalCallCount;
}
@Override
protected void onStopInternal() {
mOnStopInternalCallCount++;
}
int getOnStopInternalCallCount() {
return mOnStopInternalCallCount;
}
@Override
protected void onDestroyInternal() {
mOnDestroyInternalCallCount++;
}
int getOnDestroyInternalCallCount() {
return mOnDestroyInternalCallCount;
}
@Override
protected void updateState(Preference preference) {
mUpdateStateArg = preference;
mUpdateStateCallCount++;
}
Preference getUpdateStateArg() {
return mUpdateStateArg;
}
int getUpdateStateCallCount() {
return mUpdateStateCallCount;
}
@Override
protected boolean handlePreferenceChanged(Preference preference, Object newValue) {
mHandlePreferenceChangedCallCount++;
mHandlePreferenceChangedPreferenceArg = preference;
mHandlePreferenceChangedValueArg = newValue;
return super.handlePreferenceChanged(preference, newValue);
}
int getHandlePreferenceChangedCallCount() {
return mHandlePreferenceChangedCallCount;
}
Preference getHandlePreferenceChangedPreferenceArg() {
return mHandlePreferenceChangedPreferenceArg;
}
Object getHandlePreferenceChangedValueArg() {
return mHandlePreferenceChangedValueArg;
}
@Override
protected boolean handlePreferenceClicked(Preference preference) {
mHandlePreferenceClickedCallCount++;
mHandlePreferenceClickedArg = preference;
return super.handlePreferenceClicked(preference);
}
int getHandlePreferenceClickedCallCount() {
return mHandlePreferenceClickedCallCount;
}
Preference getHandlePreferenceClickedArg() {
return mHandlePreferenceClickedArg;
}
@Override
protected boolean isUxRestrictionsIgnored(boolean allIgnores, Set preferencesThatIgnore) {
return super.isUxRestrictionsIgnored(mAllIgnoresUxRestrictions,
mPreferencesIgnoringUxRestrictions);
}
protected void setUxRestrictionsIgnoredConfig(boolean allIgnore, Set preferencesThatIgnore) {
mAllIgnoresUxRestrictions = allIgnore;
mPreferencesIgnoringUxRestrictions = preferencesThatIgnore;
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (C) 2019 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceGroup;
import androidx.preference.SwitchPreference;
import androidx.preference.TwoStatePreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class GroupSelectionPreferenceControllerTest {
private static class TestGroupSelectionPreferenceController extends
GroupSelectionPreferenceController {
private String mCurrentCheckedKey;
private boolean mAllowPassThrough = true;
private List<TwoStatePreference> mGroupPreferences;
TestGroupSelectionPreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
mGroupPreferences = new ArrayList<>();
}
@Override
protected String getCurrentCheckedKey() {
return mCurrentCheckedKey;
}
@Override
@NonNull
protected List<TwoStatePreference> getGroupPreferences() {
return mGroupPreferences;
}
@Override
protected boolean handleGroupItemSelected(TwoStatePreference preference) {
if (mAllowPassThrough) {
mCurrentCheckedKey = preference.getKey();
return true;
} else {
return false;
}
}
public void setCurrentCheckedKey(String key) {
mCurrentCheckedKey = key;
}
public void setAllowPassThrough(boolean allow) {
mAllowPassThrough = allow;
}
public void setGroupPreferences(List<TwoStatePreference> preferences) {
mGroupPreferences = preferences;
}
}
private Context mContext;
private PreferenceGroup mPreferenceGroup;
private PreferenceControllerTestHelper<TestGroupSelectionPreferenceController>
mPreferenceControllerHelper;
private TestGroupSelectionPreferenceController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreferenceGroup = new LogicalPreferenceGroup(mContext);
mPreferenceControllerHelper = new PreferenceControllerTestHelper<>(mContext,
TestGroupSelectionPreferenceController.class, mPreferenceGroup);
mController = mPreferenceControllerHelper.getController();
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
@Test
public void refreshUi_hasTwoElements() {
List<TwoStatePreference> entries = new ArrayList<>();
entries.add(createPreference("key1"));
entries.add(createPreference("key2"));
mController.setGroupPreferences(entries);
mController.refreshUi();
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
}
@Test
public void refreshUi_elementChecked() {
List<TwoStatePreference> entries = new ArrayList<>();
entries.add(createPreference("key1"));
entries.add(createPreference("key2"));
mController.setGroupPreferences(entries);
mController.setCurrentCheckedKey("key2");
mController.refreshUi();
assertThat(findPreference("key1").isChecked()).isFalse();
assertThat(findPreference("key2").isChecked()).isTrue();
}
@Test
public void performClick_allowPassThrough_checkedElementChanged() {
List<TwoStatePreference> entries = new ArrayList<>();
entries.add(createPreference("key1"));
entries.add(createPreference("key2"));
mController.setGroupPreferences(entries);
mController.setCurrentCheckedKey("key2");
mController.setAllowPassThrough(true);
mController.refreshUi();
assertThat(findPreference("key1").isChecked()).isFalse();
assertThat(findPreference("key2").isChecked()).isTrue();
findPreference("key1").performClick();
assertThat(findPreference("key1").isChecked()).isTrue();
assertThat(findPreference("key2").isChecked()).isFalse();
}
@Test
public void performClick_disallowPassThrough_checkedElementNotChanged() {
List<TwoStatePreference> entries = new ArrayList<>();
entries.add(createPreference("key1"));
entries.add(createPreference("key2"));
mController.setGroupPreferences(entries);
mController.setCurrentCheckedKey("key2");
mController.setAllowPassThrough(false);
mController.refreshUi();
assertThat(findPreference("key1").isChecked()).isFalse();
assertThat(findPreference("key2").isChecked()).isTrue();
findPreference("key1").performClick();
assertThat(findPreference("key1").isChecked()).isFalse();
assertThat(findPreference("key2").isChecked()).isTrue();
}
@Test
public void performClick_disallowPassThrough_notifyKeyChanged_checkedElementChanged() {
List<TwoStatePreference> entries = new ArrayList<>();
entries.add(createPreference("key1"));
entries.add(createPreference("key2"));
mController.setGroupPreferences(entries);
mController.setCurrentCheckedKey("key2");
mController.setAllowPassThrough(false);
mController.refreshUi();
assertThat(findPreference("key1").isChecked()).isFalse();
assertThat(findPreference("key2").isChecked()).isTrue();
findPreference("key1").performClick();
assertThat(findPreference("key1").isChecked()).isFalse();
assertThat(findPreference("key2").isChecked()).isTrue();
mController.setCurrentCheckedKey("key1");
mController.notifyCheckedKeyChanged();
assertThat(findPreference("key1").isChecked()).isTrue();
assertThat(findPreference("key2").isChecked()).isFalse();
}
private TwoStatePreference findPreference(String key) {
return mPreferenceGroup.findPreference(key);
}
private TwoStatePreference createPreference(String key) {
TwoStatePreference preference = new SwitchPreference(mContext);
preference.setKey(key);
return preference;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2019 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.text.InputType;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import androidx.preference.EditTextPreference;
import androidx.preference.PreferenceFragmentCompat;
import com.android.car.settings.R;
import com.android.car.settings.testutils.BaseTestActivity;
import com.android.car.ui.preference.EditTextPreferenceDialogFragment;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.ShadowAlertDialog;
/** Unit test for {@link EditTextPreferenceDialogFragment}. */
@RunWith(RobolectricTestRunner.class)
public class PasswordEditTextPreferenceDialogFragmentTest {
private Context mContext;
private ActivityController<BaseTestActivity> mTestActivityController;
private BaseTestActivity mTestActivity;
private EditTextPreference mPreference;
private PasswordEditTextPreferenceDialogFragment mFragment;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
Robolectric.getForegroundThreadScheduler().pause();
mTestActivityController = ActivityController.of(new BaseTestActivity());
mTestActivity = mTestActivityController.get();
mTestActivityController.setup();
TestTargetFragment targetFragment = new TestTargetFragment();
mTestActivity.launchFragment(targetFragment);
mPreference = new PasswordEditTextPreference(mContext);
mPreference.setDialogLayoutResource(R.layout.preference_dialog_password_edittext);
mPreference.setKey("key");
Robolectric.getForegroundThreadScheduler().unPause();
targetFragment.getPreferenceScreen().addPreference(mPreference);
mFragment = PasswordEditTextPreferenceDialogFragment.newInstance(mPreference.getKey());
mFragment.setTargetFragment(targetFragment, /* requestCode= */ 0);
}
@Test
public void onStart_inputTypeSetToPassword_shouldRevealShowPasswordCheckBoxUnchecked() {
mFragment.show(mTestActivity.getSupportFragmentManager(), /* tag= */ null);
AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog();
CheckBox checkBox = dialog.findViewById(R.id.checkbox);
assertThat(checkBox.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(!checkBox.isChecked()).isTrue();
}
@Test
public void onCheckBoxChecked_shouldRevealRawPassword() {
String testPassword = "TEST_PASSWORD";
mFragment.show(mTestActivity.getSupportFragmentManager(), /* tag= */ null);
AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog();
CheckBox checkBox = dialog.findViewById(R.id.checkbox);
EditText editText = dialog.findViewById(android.R.id.edit);
editText.setText(testPassword);
checkBox.performClick();
assertThat(editText.getInputType()).isEqualTo(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
assertThat(editText.getText().toString()).isEqualTo(testPassword);
}
@Test
public void onCheckBoxUnchecked_shouldObscureRawPassword() {
String testPassword = "TEST_PASSWORD";
mFragment.show(mTestActivity.getSupportFragmentManager(), /* tag= */ null);
AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog();
CheckBox checkBox = dialog.findViewById(R.id.checkbox);
EditText editText = dialog.findViewById(android.R.id.edit);
editText.setText(testPassword);
// Performing click twice to simulate uncheck
checkBox.performClick();
checkBox.performClick();
assertThat(editText.getInputType()).isEqualTo((InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD));
assertThat(editText.getText().toString()).isEqualTo(testPassword);
}
/** Simple {@link PreferenceFragmentCompat} implementation to serve as the target fragment. */
public static class TestTargetFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2018 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertThrows;
import android.car.drivingstate.CarUxRestrictions;
import com.android.car.settings.R;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Unit test for {@link PreferenceControllerListHelper}.
*/
@RunWith(RobolectricTestRunner.class)
public class PreferenceControllerListHelperTest {
private static final CarUxRestrictions UX_RESTRICTIONS =
new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
@Test
public void getControllers_returnsList() {
List<String> validKeys = Arrays.asList("key1", "key2");
List<PreferenceController> controllers =
PreferenceControllerListHelper.getPreferenceControllersFromXml(
RuntimeEnvironment.application,
R.xml.preference_controller_list_helper_success,
mock(FragmentController.class),
UX_RESTRICTIONS);
assertThat(controllers).hasSize(validKeys.size());
List<String> foundKeys = new ArrayList<>();
for (PreferenceController controller : controllers) {
assertThat(controller).isInstanceOf(DefaultRestrictionsPreferenceController.class);
foundKeys.add(controller.getPreferenceKey());
}
assertThat(foundKeys).containsAtLeastElementsIn(validKeys);
}
@Test
public void getControllers_invalidController_throwsIllegalArgumentException() {
assertThrows(IllegalArgumentException.class,
() -> PreferenceControllerListHelper.getPreferenceControllersFromXml(
RuntimeEnvironment.application,
R.xml.preference_controller_list_helper_fail_invalid_controller,
mock(FragmentController.class), UX_RESTRICTIONS));
}
@Test
public void getControllers_missingKey_throwsIllegalArgumentException() {
assertThrows(IllegalArgumentException.class,
() -> PreferenceControllerListHelper.getPreferenceControllersFromXml(
RuntimeEnvironment.application,
R.xml.preference_controller_list_helper_fail_missing_key,
mock(FragmentController.class), UX_RESTRICTIONS));
}
@Test
public void getControllers_returns_zoneAvailabilityList() {
List<Integer> validAvailabilities = Arrays.asList(
PreferenceController.AVAILABLE, PreferenceController.CONDITIONALLY_UNAVAILABLE);
List<PreferenceController> controllers =
PreferenceControllerListHelper.getPreferenceControllersFromXml(
RuntimeEnvironment.application,
R.xml.preference_controller_list_helper_success_occupants,
mock(FragmentController.class),
UX_RESTRICTIONS);
assertThat(controllers).hasSize(validAvailabilities.size());
List<Integer> foundAvailabilities = new ArrayList<>();
for (PreferenceController controller : controllers) {
assertThat(controller).isInstanceOf(DefaultRestrictionsPreferenceController.class);
foundAvailabilities.add(controller.getAvailabilityStatus());
}
assertThat(foundAvailabilities).containsAtLeastElementsIn(validAvailabilities);
}
@Test
public void getControllers_invalidZoneAvailiabilityStatus_throwsIllegalArgumentException() {
assertThrows(IllegalArgumentException.class,
() -> PreferenceControllerListHelper.getPreferenceControllersFromXml(
RuntimeEnvironment.application,
R.xml.preference_controller_list_helper_fail_invalid_occupants,
mock(FragmentController.class), UX_RESTRICTIONS));
}
}

View File

@@ -0,0 +1,410 @@
/*
* Copyright 2018 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.car.settings.common;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.android.car.settings.common.PreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertThrows;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.HashSet;
import java.util.Set;
/**
* Unit test for {@link PreferenceController}.
*/
@RunWith(RobolectricTestRunner.class)
public class PreferenceControllerTest {
private static final CarUxRestrictions NO_SETUP_UX_RESTRICTIONS =
new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP, /* timestamp= */ 0).build();
private static final CarUxRestrictions BASELINE_UX_RESTRICTIONS =
new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
private PreferenceControllerTestHelper<FakePreferenceController> mControllerHelper;
private FakePreferenceController mController;
private Context mContext;
@Mock
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
FakePreferenceController.class, mPreference);
mController = mControllerHelper.getController();
}
@Test
public void setPreference_wrongType_throwsIllegalArgumentException() {
PreferenceControllerTestHelper<WrongTypePreferenceController> controllerHelper =
new PreferenceControllerTestHelper<>(mContext, WrongTypePreferenceController.class);
assertThrows(IllegalArgumentException.class, () -> controllerHelper.setPreference(
new Preference(mContext)));
}
@Test
public void setPreference_correctType_setsPreference() {
assertThat(mController.getPreference()).isEqualTo(mPreference);
}
@Test
public void setPreference_callsCheckInitialized() {
assertThat(mController.getCheckInitializedCallCount()).isEqualTo(1);
}
@Test
public void setPreference_registersOnPreferenceChangeListener() {
ArgumentCaptor<Preference.OnPreferenceChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(Preference.OnPreferenceChangeListener.class);
Object newValue = new Object();
verify(mPreference).setOnPreferenceChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onPreferenceChange(mPreference, newValue);
assertThat(mController.getHandlePreferenceChangedCallCount()).isEqualTo(1);
assertThat(mController.getHandlePreferenceChangedPreferenceArg()).isEqualTo(mPreference);
assertThat(mController.getHandlePreferenceChangedValueArg()).isEqualTo(newValue);
}
@Test
public void setPreference_registersOnPreferenceClickListener() {
ArgumentCaptor<Preference.OnPreferenceClickListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(Preference.OnPreferenceClickListener.class);
verify(mPreference).setOnPreferenceClickListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onPreferenceClick(mPreference);
assertThat(mController.getHandlePreferenceClickedCallCount()).isEqualTo(1);
assertThat(mController.getHandlePreferenceClickedArg()).isEqualTo(mPreference);
}
@Test
public void onUxRestrictionsChanged_updatesUxRestrictions() {
mController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
assertThat(mController.getUxRestrictions()).isEqualTo(NO_SETUP_UX_RESTRICTIONS);
}
@Test
public void onUxRestrictionsChanged_created_restricted_preferenceDisabled() {
mControllerHelper.markState(Lifecycle.State.CREATED);
mController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
verify(mPreference).setEnabled(false);
}
@Test
public void onUxRestrictionsChanged_created_restricted_unrestricted_preferenceEnabled() {
InOrder orderVerifier = inOrder(mPreference);
mControllerHelper.markState(Lifecycle.State.CREATED);
mController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
mController.onUxRestrictionsChanged(BASELINE_UX_RESTRICTIONS);
// setEnabled(true) called on Create.
orderVerifier.verify(mPreference).setEnabled(true);
// setEnabled(false) called with the first UXR change event.
orderVerifier.verify(mPreference).setEnabled(false);
// setEnabled(true) called with the second UXR change event.
orderVerifier.verify(mPreference).setEnabled(true);
}
@Test
public void onUxRestrictionsChanged_restricted_allPreferencesIgnore_preferenceEnabled() {
// mPreference cannot be a Mock here because its real methods need to be invoked.
mPreference = new Preference(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
FakePreferenceController.class, mPreference);
mController = mControllerHelper.getController();
Set preferencesIgnoringUxRestrictions = new HashSet();
mController.setUxRestrictionsIgnoredConfig(/* allIgnores= */ true,
preferencesIgnoringUxRestrictions);
mControllerHelper.markState(Lifecycle.State.CREATED);
mController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void onUxRestrictionsChanged_restricted_thisPreferenceIgnores_preferenceEnabled() {
// mPreference cannot be a Mock here because its real methods need to be invoked.
mPreference = new Preference(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
FakePreferenceController.class, mPreference);
mController = mControllerHelper.getController();
Set preferencesIgnoringUxRestrictions = new HashSet();
preferencesIgnoringUxRestrictions.add(PreferenceControllerTestHelper.getKey());
mController.setUxRestrictionsIgnoredConfig(/* allIgnores= */ false,
preferencesIgnoringUxRestrictions);
mControllerHelper.markState(Lifecycle.State.CREATED);
mController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void onUxRestrictionsChanged_restricted_uxRestrictionsNotIgnored_preferenceDisabled() {
// mPreference cannot be a Mock here because its real methods need to be invoked.
mPreference = new Preference(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
FakePreferenceController.class, mPreference);
mController = mControllerHelper.getController();
Set preferencesIgnoringUxRestrictions = new HashSet();
mController.setUxRestrictionsIgnoredConfig(/* allIgnores= */ false,
preferencesIgnoringUxRestrictions);
mControllerHelper.markState(Lifecycle.State.CREATED);
mController.onUxRestrictionsChanged(NO_SETUP_UX_RESTRICTIONS);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void getAvailabilityStatus_defaultsToAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_defaultsToAvailable_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_defaultsToAvailable_zoneRead() {
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_defaultsToAvailable_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_defaultsToConditionallyUnavailable_zoneWrite() {
mController.setAvailabilityStatus(CONDITIONALLY_UNAVAILABLE);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_defaultsToConditionallyUnavailable_zoneRead() {
mController.setAvailabilityStatus(CONDITIONALLY_UNAVAILABLE);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_defaultsToConditionallyUnavailable_zoneHidden() {
mController.setAvailabilityStatus(CONDITIONALLY_UNAVAILABLE);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_defaultsToUnsupportedOnDevice_zoneWrite() {
mController.setAvailabilityStatus(UNSUPPORTED_ON_DEVICE);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_defaultsToUnsupportedOnDevice_zoneRead() {
mController.setAvailabilityStatus(UNSUPPORTED_ON_DEVICE);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_defaultsToUnsupportedOnDevice_zoneHidden() {
mController.setAvailabilityStatus(UNSUPPORTED_ON_DEVICE);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_defaultsToDisabledForProfile_zoneWrite() {
mController.setAvailabilityStatus(DISABLED_FOR_PROFILE);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_defaultsToDisabledForProfile_zoneRead() {
mController.setAvailabilityStatus(DISABLED_FOR_PROFILE);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_defaultsToDisabledForProfile_zoneHidden() {
mController.setAvailabilityStatus(DISABLED_FOR_PROFILE);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_defaultsToAvailableForViewing_zoneWrite() {
mController.setAvailabilityStatus(AVAILABLE_FOR_VIEWING);
mController.setAvailabilityStatusForZone("write");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_defaultsToAvailableForViewing_zoneRead() {
mController.setAvailabilityStatus(AVAILABLE_FOR_VIEWING);
mController.setAvailabilityStatusForZone("read");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_defaultsToAvailableForViewing_zoneHidden() {
mController.setAvailabilityStatus(AVAILABLE_FOR_VIEWING);
mController.setAvailabilityStatusForZone("hidden");
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void lifecycle_unsupportedOnDevice_doesNotCallSubclassHooks() {
mController.setAvailabilityStatus(UNSUPPORTED_ON_DEVICE);
mControllerHelper.markState(Lifecycle.State.STARTED);
mControllerHelper.markState(Lifecycle.State.DESTROYED);
assertThat(mController.getOnCreateInternalCallCount()).isEqualTo(0);
assertThat(mController.getOnStartInternalCallCount()).isEqualTo(0);
assertThat(mController.getOnResumeInternalCallCount()).isEqualTo(0);
assertThat(mController.getOnPauseInternalCallCount()).isEqualTo(0);
assertThat(mController.getOnStopInternalCallCount()).isEqualTo(0);
assertThat(mController.getOnDestroyInternalCallCount()).isEqualTo(0);
}
@Test
public void onCreate_unsupportedOnDevice_hidesPreference() {
mController.setAvailabilityStatus(UNSUPPORTED_ON_DEVICE);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
verify(mPreference).setVisible(false);
}
@Test
public void onCreate_callsSubclassHook() {
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
assertThat(mController.getOnCreateInternalCallCount()).isEqualTo(1);
}
@Test
public void onStart_callsSubclassHook() {
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mController.getOnStartInternalCallCount()).isEqualTo(1);
}
@Test
public void onResume_callsSubclassHook() {
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_RESUME);
assertThat(mController.getOnResumeInternalCallCount()).isEqualTo(1);
}
@Test
public void onPause_callsSubclassHook() {
mControllerHelper.markState(Lifecycle.State.RESUMED);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_PAUSE);
assertThat(mController.getOnPauseInternalCallCount()).isEqualTo(1);
}
@Test
public void onStop_callsSubclassHook() {
mControllerHelper.markState(Lifecycle.State.STARTED);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
assertThat(mController.getOnStopInternalCallCount()).isEqualTo(1);
}
@Test
public void onDestroy_callsSubclassHook() {
mControllerHelper.markState(Lifecycle.State.STARTED);
mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_DESTROY);
assertThat(mController.getOnDestroyInternalCallCount()).isEqualTo(1);
}
@Test
public void handlePreferenceChanged_defaultReturnsTrue() {
assertThat(mController.handlePreferenceChanged(mPreference, new Object())).isTrue();
}
@Test
public void handlePreferenceClicked_defaultReturnsFalse() {
assertThat(mController.handlePreferenceClicked(mPreference)).isFalse();
}
/** For testing passing the wrong type of preference to the controller. */
private static class WrongTypePreferenceController extends
PreferenceController<PreferenceGroup> {
WrongTypePreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
}
@Override
protected Class<PreferenceGroup> getPreferenceType() {
return PreferenceGroup.class;
}
}
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright 2018 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.car.settings.common;
import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import static androidx.lifecycle.Lifecycle.State.CREATED;
import static androidx.lifecycle.Lifecycle.State.DESTROYED;
import static androidx.lifecycle.Lifecycle.State.INITIALIZED;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static androidx.lifecycle.Lifecycle.State.STARTED;
import static org.mockito.Mockito.mock;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
/**
* Helper for testing {@link PreferenceController} classes.
*
* @param <T> the type of preference controller under test.
*/
public class PreferenceControllerTestHelper<T extends PreferenceController> {
private static final String PREFERENCE_KEY = "preference_key";
private static final CarUxRestrictions UX_RESTRICTIONS =
new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
private Lifecycle.State mState = INITIALIZED;
private final FragmentController mMockFragmentController;
private final T mPreferenceController;
private final PreferenceScreen mScreen;
private boolean mSetPreferenceCalled;
/**
* Constructs a new helper. Call {@link #setPreference(Preference)} once initialization on the
* controller is complete to associate the controller with a preference.
*
* @param context the {@link Context} to use to instantiate the preference
* controller.
* @param preferenceControllerType the class type under test.
*/
public PreferenceControllerTestHelper(Context context, Class<T> preferenceControllerType) {
mMockFragmentController = mock(FragmentController.class);
mPreferenceController = ReflectionHelpers.callConstructor(preferenceControllerType,
ClassParameter.from(Context.class, context),
ClassParameter.from(String.class, PREFERENCE_KEY),
ClassParameter.from(FragmentController.class, mMockFragmentController),
ClassParameter.from(CarUxRestrictions.class, UX_RESTRICTIONS));
mScreen = new PreferenceManager(context).createPreferenceScreen(context);
}
/**
* Convenience constructor for a new helper for controllers which do not need to do additional
* initialization before a preference is set.
*
* @param preference the {@link Preference} to associate with the controller.
*/
public PreferenceControllerTestHelper(Context context, Class<T> preferenceControllerType,
Preference preference) {
this(context, preferenceControllerType);
setPreference(preference);
}
/**
* Associates the controller with the given preference. This should only be called once.
*/
public void setPreference(Preference preference) {
if (mSetPreferenceCalled) {
throw new IllegalStateException(
"setPreference should only be called once. Create a new helper if needed.");
}
preference.setKey(PREFERENCE_KEY);
mScreen.addPreference(preference);
mPreferenceController.setPreference(preference);
mSetPreferenceCalled = true;
}
/**
* Returns the {@link PreferenceController} of this helper.
*/
public T getController() {
return mPreferenceController;
}
/**
* Returns a mock {@link FragmentController} that can be used to verify controller navigation
* and stub finding dialog fragments.
*/
public FragmentController getMockFragmentController() {
return mMockFragmentController;
}
/**
* Sends a {@link Lifecycle.Event} to the controller. This is preferred over calling the
* controller's lifecycle methods directly as it ensures intermediate events are dispatched.
* For example, sending {@link Lifecycle.Event#ON_START} to an
* {@link Lifecycle.State#INITIALIZED} controller will dispatch
* {@link Lifecycle.Event#ON_CREATE} and {@link Lifecycle.Event#ON_START} while moving the
* controller to the {@link Lifecycle.State#STARTED} state.
*/
public void sendLifecycleEvent(Lifecycle.Event event) {
markState(getStateAfter(event));
}
/**
* Move the {@link PreferenceController} to the given {@code state}. This is preferred over
* calling the controller's lifecycle methods directly as it ensures intermediate events are
* dispatched. For example, marking the {@link Lifecycle.State#STARTED} state on an
* {@link Lifecycle.State#INITIALIZED} controller will also send the
* {@link Lifecycle.Event#ON_CREATE} and {@link Lifecycle.Event#ON_START} events.
*/
public void markState(Lifecycle.State state) {
while (mState != state) {
while (mState.compareTo(state) > 0) {
dispatchEvent(downEvent(mState));
}
while (mState.compareTo(state) < 0) {
dispatchEvent(upEvent(mState));
}
}
}
public static String getKey() {
return PREFERENCE_KEY;
}
/*
* Ideally we would use androidx.lifecycle.LifecycleRegistry to drive the lifecycle changes.
* However, doing so led to test flakiness with an unknown root cause. We dispatch state
* changes manually for now, borrowing from LifecycleRegistry's implementation, pending
* further investigation.
*/
@NonNull
private Lifecycle getLifecycle() {
throw new UnsupportedOperationException();
}
private void dispatchEvent(Lifecycle.Event event) {
switch (event) {
case ON_CREATE:
mScreen.onAttached();
mPreferenceController.onCreate(this::getLifecycle);
break;
case ON_START:
mPreferenceController.onStart(this::getLifecycle);
break;
case ON_RESUME:
mPreferenceController.onResume(this::getLifecycle);
break;
case ON_PAUSE:
mPreferenceController.onPause(this::getLifecycle);
break;
case ON_STOP:
mPreferenceController.onStop(this::getLifecycle);
break;
case ON_DESTROY:
mScreen.onDetached();
mPreferenceController.onDestroy(this::getLifecycle);
break;
case ON_ANY:
throw new IllegalArgumentException();
}
mState = getStateAfter(event);
}
private static Lifecycle.State getStateAfter(Lifecycle.Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException("Unexpected event value " + event);
}
private static Lifecycle.Event downEvent(Lifecycle.State state) {
switch (state) {
case INITIALIZED:
throw new IllegalArgumentException();
case CREATED:
return ON_DESTROY;
case STARTED:
return ON_STOP;
case RESUMED:
return ON_PAUSE;
case DESTROYED:
throw new IllegalArgumentException();
}
throw new IllegalArgumentException("Unexpected state value " + state);
}
private static Lifecycle.Event upEvent(Lifecycle.State state) {
switch (state) {
case INITIALIZED:
case DESTROYED:
return ON_CREATE;
case CREATED:
return ON_START;
case STARTED:
return ON_RESUME;
case RESUMED:
throw new IllegalArgumentException();
}
throw new IllegalArgumentException("Unexpected state value " + state);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import androidx.preference.TwoStatePreference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class PreferenceUtilTest {
@Test
public void testCheckPreferenceType_true() {
Preference preference = new SwitchPreference(RuntimeEnvironment.application);
assertThat(PreferenceUtil.checkPreferenceType(preference, SwitchPreference.class)).isTrue();
}
@Test
public void testCheckPreferenceType_superclass_true() {
Preference preference = new SwitchPreference(RuntimeEnvironment.application);
assertThat(
PreferenceUtil.checkPreferenceType(preference, TwoStatePreference.class)).isTrue();
}
@Test
public void testCheckPreferenceType_false() {
Preference preference = new ListPreference(RuntimeEnvironment.application);
assertThat(
PreferenceUtil.checkPreferenceType(preference, TwoStatePreference.class)).isFalse();
}
// Test should succeed without throwing an exception.
@Test
public void testRequirePreferenceType_true() {
Preference preference = new SwitchPreference(RuntimeEnvironment.application);
PreferenceUtil.requirePreferenceType(preference, SwitchPreference.class);
}
// Test should succeed without throwing an exception.
@Test
public void testRequirePreferenceType_superclass_true() {
Preference preference = new SwitchPreference(RuntimeEnvironment.application);
PreferenceUtil.requirePreferenceType(preference, TwoStatePreference.class);
}
@Test
public void testRequirePreferenceType_false() {
Preference preference = new ListPreference(RuntimeEnvironment.application);
assertThrows(IllegalArgumentException.class,
() -> PreferenceUtil.requirePreferenceType(preference, TwoStatePreference.class));
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2018 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import com.android.car.settings.R;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.List;
/**
* Unit test for {@link PreferenceXmlParser}.
*/
@RunWith(RobolectricTestRunner.class)
public class PreferenceXmlParserTest {
@Test
public void extractMetadata_keyAndControllerName() throws IOException, XmlPullParserException {
List<Bundle> metadata = PreferenceXmlParser.extractMetadata(
RuntimeEnvironment.application, R.xml.preference_parser,
PreferenceXmlParser.MetadataFlag.FLAG_NEED_KEY
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_CONTROLLER);
assertThat(metadata).hasSize(4);
for (Bundle bundle : metadata) {
assertThat(bundle.getString(PreferenceXmlParser.METADATA_KEY)).isNotNull();
assertThat(bundle.getString(PreferenceXmlParser.METADATA_CONTROLLER)).isNotNull();
}
}
@Test
public void extractMetadata_driverZone() throws IOException, XmlPullParserException {
List<Bundle> metadata = PreferenceXmlParser.extractMetadata(
RuntimeEnvironment.application, R.xml.preference_occupants_parser,
PreferenceXmlParser.MetadataFlag.FLAG_NEED_KEY
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_CONTROLLER
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_DRIVER);
assertThat(metadata).hasSize(3);
assertThat(metadata.get(1).getString(PreferenceXmlParser.METADATA_OCCUPANT_ZONE))
.isEqualTo(PreferenceXmlParser.PREF_AVAILABILITY_STATUS_WRITE);
}
@Test
public void extractMetadata_frontPassengerZone() throws IOException, XmlPullParserException {
List<Bundle> metadata = PreferenceXmlParser.extractMetadata(
RuntimeEnvironment.application, R.xml.preference_occupants_parser,
PreferenceXmlParser.MetadataFlag.FLAG_NEED_KEY
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_CONTROLLER
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_FRONT_PASSENGER);
assertThat(metadata).hasSize(3);
assertThat(metadata.get(1).getString(PreferenceXmlParser.METADATA_OCCUPANT_ZONE))
.isEqualTo(PreferenceXmlParser.PREF_AVAILABILITY_STATUS_READ);
}
@Test
public void extractMetadata_rearPassengerZone() throws IOException, XmlPullParserException {
List<Bundle> metadata = PreferenceXmlParser.extractMetadata(
RuntimeEnvironment.application, R.xml.preference_occupants_parser,
PreferenceXmlParser.MetadataFlag.FLAG_NEED_KEY
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_CONTROLLER
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_REAR_PASSENGER);
assertThat(metadata).hasSize(3);
assertThat(metadata.get(1).getString(PreferenceXmlParser.METADATA_OCCUPANT_ZONE))
.isEqualTo(PreferenceXmlParser.PREF_AVAILABILITY_STATUS_HIDDEN);
}
@Test
public void extractMetadata_occupantDriverIsNull() throws IOException, XmlPullParserException {
List<Bundle> metadata = PreferenceXmlParser.extractMetadata(
RuntimeEnvironment.application, R.xml.preference_occupants_parser,
PreferenceXmlParser.MetadataFlag.FLAG_NEED_KEY
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_CONTROLLER
| PreferenceXmlParser.MetadataFlag.FLAG_NEED_PREF_DRIVER);
assertThat(metadata).hasSize(3);
assertThat(metadata.get(2).getString(PreferenceXmlParser.METADATA_OCCUPANT_ZONE)).isNull();
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2019 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
import com.android.car.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class ProgressBarPreferenceTest {
private static final String TEST_LABEL = "TEST_LABEL";
private Context mContext;
private PreferenceViewHolder mViewHolder;
private ProgressBarPreference mProgressBarPreference;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
View rootView = View.inflate(mContext, R.layout.progress_bar_preference,
/* root= */ null);
mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView);
mProgressBarPreference = new ProgressBarPreference(mContext);
}
@Test
public void setMinLabel_setsText() {
mProgressBarPreference.setMinLabel(TEST_LABEL);
mProgressBarPreference.onBindViewHolder(mViewHolder);
assertThat(getMinLabel().getText()).isEqualTo(TEST_LABEL);
}
@Test
public void setMaxLabel_setsText() {
mProgressBarPreference.setMaxLabel(TEST_LABEL);
mProgressBarPreference.onBindViewHolder(mViewHolder);
assertThat(getMaxLabel().getText()).isEqualTo(TEST_LABEL);
}
@Test
public void setMin_setsMin() {
mProgressBarPreference.setMin(10);
mProgressBarPreference.onBindViewHolder(mViewHolder);
assertThat(getProgressBar().getMin()).isEqualTo(10);
}
@Test
public void setMax_setsMax() {
mProgressBarPreference.setMax(1000);
mProgressBarPreference.onBindViewHolder(mViewHolder);
assertThat(getProgressBar().getMax()).isEqualTo(1000);
}
@Test
public void setProgress_setsProgress() {
mProgressBarPreference.setProgress(40);
mProgressBarPreference.onBindViewHolder(mViewHolder);
assertThat(getProgressBar().getProgress()).isEqualTo(40);
}
private ProgressBar getProgressBar() {
return (ProgressBar) mViewHolder.findViewById(android.R.id.progress);
}
private TextView getMinLabel() {
return (TextView) mViewHolder.findViewById(android.R.id.text1);
}
private TextView getMaxLabel() {
return (TextView) mViewHolder.findViewById(android.R.id.text2);
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2019 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.car.settings.common;
import static com.google.common.truth.Truth.assertThat;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import androidx.preference.EditTextPreference;
import androidx.preference.PreferenceFragmentCompat;
import com.android.car.settings.R;
import com.android.car.settings.testutils.BaseTestActivity;
import com.android.car.ui.preference.EditTextPreferenceDialogFragment;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowWindow;
/** Unit test for {@link EditTextPreferenceDialogFragment}. */
@RunWith(RobolectricTestRunner.class)
public class ValidatedEditTextPreferenceDialogFragmentTest {
private Context mContext;
private ActivityController<BaseTestActivity> mTestActivityController;
private BaseTestActivity mTestActivity;
private EditTextPreference mPreference;
private ValidatedEditTextPreferenceDialogFragment mFragment;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
Robolectric.getForegroundThreadScheduler().pause();
mTestActivityController = ActivityController.of(new BaseTestActivity());
mTestActivity = mTestActivityController.get();
mTestActivityController.setup();
TestTargetFragment targetFragment = new TestTargetFragment();
mTestActivity.launchFragment(targetFragment);
mPreference = new ValidatedEditTextPreference(mContext);
mPreference.setDialogLayoutResource(R.layout.preference_dialog_edittext);
mPreference.setKey("key");
Robolectric.getForegroundThreadScheduler().unPause();
targetFragment.getPreferenceScreen().addPreference(mPreference);
mFragment = ValidatedEditTextPreferenceDialogFragment
.newInstance(mPreference.getKey());
mFragment.setTargetFragment(targetFragment, /* requestCode= */ 0);
}
@Test
public void noValidatorSet_shouldEnablePositiveButton_and_allowEnterToSubmit() {
mFragment.show(mTestActivity.getSupportFragmentManager(), /* tag= */ null);
Button positiveButton = ShadowAlertDialog.getLatestAlertDialog().getButton(
DialogInterface.BUTTON_POSITIVE);
EditText editText = ShadowAlertDialog.getLatestAlertDialog().findViewById(
android.R.id.edit);
assertThat(positiveButton.isEnabled()).isTrue();
assertThat(mFragment.getAllowEnterToSubmit()).isTrue();
editText.setText("any text");
assertThat(positiveButton.isEnabled()).isTrue();
assertThat(mFragment.getAllowEnterToSubmit()).isTrue();
}
@Test
public void onInvalidInput_shouldDisablePositiveButton_and_disallowEnterToSubmit() {
((ValidatedEditTextPreference) mPreference).setValidator(
new ValidatedEditTextPreference.Validator() {
@Override
public boolean isTextValid(String value) {
return value.length() > 100;
}
});
mFragment.show(mTestActivity.getSupportFragmentManager(), /* tag= */ null);
Button positiveButton = ShadowAlertDialog.getLatestAlertDialog().getButton(
DialogInterface.BUTTON_POSITIVE);
EditText editText = ShadowAlertDialog.getLatestAlertDialog().findViewById(
android.R.id.edit);
editText.setText("shorter than 100");
assertThat(positiveButton.isEnabled()).isFalse();
assertThat(mFragment.getAllowEnterToSubmit()).isFalse();
}
@Test
public void onValidInput_shouldEnablePositiveButton_and_allowEnterToSubmit() {
((ValidatedEditTextPreference) mPreference).setValidator(
new ValidatedEditTextPreference.Validator() {
@Override
public boolean isTextValid(String value) {
return value.length() > 1;
}
});
mFragment.show(mTestActivity.getSupportFragmentManager(), /* tag= */ null);
Button positiveButton = ShadowAlertDialog.getLatestAlertDialog().getButton(
DialogInterface.BUTTON_POSITIVE);
EditText editText = ShadowAlertDialog.getLatestAlertDialog().findViewById(
android.R.id.edit);
editText.setText("longer than 1");
assertThat(positiveButton.isEnabled()).isTrue();
assertThat(mFragment.getAllowEnterToSubmit()).isTrue();
}
private ShadowWindow getShadowWindowFromDialog(AlertDialog dialog) {
return (ShadowWindow) Shadow.extract(dialog.getWindow());
}
/** Simple {@link PreferenceFragmentCompat} implementation to serve as the target fragment. */
public static class TestTargetFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
}
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright (C) 2018 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.car.settings.language;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.car.settings.R;
import com.android.car.settings.common.LogicalPreferenceGroup;
import com.android.car.settings.testutils.ShadowLocaleStore;
import com.android.internal.app.LocaleStore;
import com.android.internal.app.SuggestedLocaleAdapter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowLocaleStore.class})
public class LocalePreferenceProviderTest {
private static class Pair {
int mItemType;
LocaleStore.LocaleInfo mLocaleInfo;
Pair(int itemType, LocaleStore.LocaleInfo localeInfo) {
mItemType = itemType;
mLocaleInfo = localeInfo;
}
}
private Context mContext;
private LocalePreferenceProvider mLocalePreferenceProvider;
private LogicalPreferenceGroup mPreferenceGroup;
private HashSet mIgnorables = new HashSet();
// This list includes the expected values that should be returned by the SuggestedLocaleAdapter.
// The index i in this list represents position, the itemType represents the return value for
// getItemViewType given the index i, and mLocaleInfo represents the return value for getItem
// given the index i.
private List<Pair> mLocaleAdapterExpectedValues;
@Mock
private SuggestedLocaleAdapter mSuggestedLocaleAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mLocalePreferenceProvider = new LocalePreferenceProvider(mContext, mSuggestedLocaleAdapter);
mLocaleAdapterExpectedValues = new ArrayList<>();
// LogicalPreferenceGroup needs to be part of a PreferenceScreen in order for it to add
// additional preferences.
PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
mPreferenceGroup = new LogicalPreferenceGroup(mContext);
screen.addPreference(mPreferenceGroup);
}
@After
public void tearDown() {
ShadowLocaleStore.reset();
}
@Test
public void testPopulateBasePreference_noSubSections() {
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.US)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.UK)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.CANADA)));
prepareSuggestedLocaleAdapterMock();
mLocalePreferenceProvider.populateBasePreference(mPreferenceGroup, mIgnorables, mock(
Preference.OnPreferenceClickListener.class));
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(3);
}
@Test
public void testPopulateBasePreference_withSubSections() {
mLocaleAdapterExpectedValues.add(
new Pair(LocalePreferenceProvider.TYPE_HEADER_SUGGESTED, null));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.US)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.FRANCE)));
mLocaleAdapterExpectedValues.add(
new Pair(LocalePreferenceProvider.TYPE_HEADER_ALL_OTHERS, null));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.UK)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.CANADA)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.KOREA)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.CHINA)));
prepareSuggestedLocaleAdapterMock();
mLocalePreferenceProvider.populateBasePreference(mPreferenceGroup, mIgnorables, mock(
Preference.OnPreferenceClickListener.class));
assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
PreferenceCategory firstCategory = (PreferenceCategory) mPreferenceGroup.getPreference(0);
assertThat(firstCategory.getTitle()).isEqualTo(
mContext.getString(R.string.language_picker_list_suggested_header));
assertThat(firstCategory.getPreferenceCount()).isEqualTo(2);
PreferenceCategory secondCategory = (PreferenceCategory) mPreferenceGroup.getPreference(1);
assertThat(secondCategory.getTitle()).isEqualTo(
mContext.getString(R.string.language_picker_list_all_header));
assertThat(secondCategory.getPreferenceCount()).isEqualTo(4);
}
@Test
public void testClickListenerTriggered() {
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.US)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.UK)));
mLocaleAdapterExpectedValues.add(new Pair(LocalePreferenceProvider.TYPE_LOCALE,
LocaleStore.getLocaleInfo(Locale.CANADA)));
prepareSuggestedLocaleAdapterMock();
Preference.OnPreferenceClickListener listener = mock(
Preference.OnPreferenceClickListener.class);
mLocalePreferenceProvider.populateBasePreference(mPreferenceGroup, mIgnorables, listener);
mPreferenceGroup.getPreference(0).performClick();
verify(listener).onPreferenceClick(mPreferenceGroup.getPreference(0));
mPreferenceGroup.getPreference(1).performClick();
verify(listener).onPreferenceClick(mPreferenceGroup.getPreference(1));
mPreferenceGroup.getPreference(2).performClick();
verify(listener).onPreferenceClick(mPreferenceGroup.getPreference(2));
}
private void prepareSuggestedLocaleAdapterMock() {
for (int i = 0; i < mLocaleAdapterExpectedValues.size(); i++) {
Pair entry = mLocaleAdapterExpectedValues.get(i);
int itemType = entry.mItemType;
LocaleStore.LocaleInfo localeInfo = entry.mLocaleInfo;
when(mSuggestedLocaleAdapter.getItemViewType(i)).thenReturn(itemType);
when(mSuggestedLocaleAdapter.getItem(i)).thenReturn(localeInfo);
}
when(mSuggestedLocaleAdapter.getCount()).thenReturn(mLocaleAdapterExpectedValues.size());
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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.car.settings.profiles;
import static junit.framework.Assert.fail;
import static org.robolectric.Shadows.shadowOf;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import java.util.List;
public class BroadcastReceiverHelpers {
private BroadcastReceiverHelpers() { }
public static BroadcastReceiver getRegisteredReceiverWithActions(List<String> actions) {
ShadowApplication.Wrapper wrapper = getRegisteredReceiverWrapperWithActions(actions);
return wrapper == null ? null : wrapper.broadcastReceiver;
}
private static ShadowApplication.Wrapper getRegisteredReceiverWrapperWithActions(
List<String> actions) {
List<ShadowApplication.Wrapper> registeredReceivers =
shadowOf(RuntimeEnvironment.application).getRegisteredReceivers();
if (registeredReceivers.isEmpty()) {
return null;
}
ShadowApplication.Wrapper returnWrapper = null;
for (ShadowApplication.Wrapper wrapper : registeredReceivers) {
if (matchesActions(wrapper.getIntentFilter(), actions)) {
if (returnWrapper == null) {
returnWrapper = wrapper;
} else {
fail("There are multiple receivers registered with this IntentFilter. "
+ "This util method cannot handle multiple receivers.");
}
}
}
return returnWrapper;
}
private static boolean matchesActions(IntentFilter filter, List<String> actions) {
if (filter.countActions() != actions.size()) {
return false;
}
for (String action : actions) {
if (!filter.matchAction(action)) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,553 @@
/*
* Copyright (C) 2019 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.car.settings.profiles;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAliveUsers;
import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.car.user.CarUserManager;
import android.car.user.UserCreationResult;
import android.car.user.UserRemovalResult;
import android.car.user.UserSwitchResult;
import android.car.util.concurrent.AndroidAsyncFuture;
import android.car.util.concurrent.AndroidFuture;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.car.settings.testutils.ShadowActivityManager;
import com.android.car.settings.testutils.ShadowUserIconProvider;
import com.android.car.settings.testutils.ShadowUserManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowProcess;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class, ShadowUserIconProvider.class})
public class ProfileHelperTest {
private static final String DEFAULT_ADMIN_NAME = "default_admin";
private static final String DEFAULT_GUEST_NAME = "default_guest";
private Context mContext;
private ProfileHelper mProfileHelper;
@Mock
private UserManager mMockUserManager;
@Mock
private Resources mMockResources;
@Mock
private CarUserManager mMockCarUserManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mProfileHelper = new ProfileHelper(mMockUserManager, mMockResources,
DEFAULT_ADMIN_NAME, DEFAULT_GUEST_NAME, mMockCarUserManager);
when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS))
.thenReturn(false);
when(mMockUserManager.isDemoUser()).thenReturn(false);
when(mMockUserManager.isGuestUser()).thenReturn(false);
ShadowUserManager.setIsHeadlessSystemUserMode(true);
ShadowActivityManager.setCurrentUser(10);
}
@After
public void tearDown() {
ShadowActivityManager.reset();
ShadowUserManager.reset();
}
@Test
public void canCurrentProcessModifyAccounts_baseline_returnsTrue() {
assertThat(mProfileHelper.canCurrentProcessModifyAccounts()).isTrue();
}
@Test
public void canCurrentProcessModifyAccounts_hasDisallowModifyAccounts_returnsFalse() {
when(mMockUserManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS))
.thenReturn(true);
assertThat(mProfileHelper.canCurrentProcessModifyAccounts()).isFalse();
}
@Test
public void canCurrentProcessModifyAccounts_isDemoUser_returnsFalse() {
when(mMockUserManager.isDemoUser()).thenReturn(true);
assertThat(mProfileHelper.canCurrentProcessModifyAccounts()).isFalse();
}
@Test
public void canCurrentProcessModifyAccounts_isGuestUser_returnsFalse() {
when(mMockUserManager.isGuestUser()).thenReturn(true);
assertThat(mProfileHelper.canCurrentProcessModifyAccounts()).isFalse();
}
@Test
public void testGetAllsers() {
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two admin, and two non-admin users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createNonAdminUser(fgUserId);
UserInfo user2 = createAdminUser(fgUserId + 1);
UserInfo user3 = createNonAdminUser(fgUserId + 2);
UserInfo user4 = createAdminUser(fgUserId + 3);
mockGetUsers(systemUser, fgUser, user2, user3, user4);
// Should return all non-system users
assertThat(mProfileHelper.getAllProfiles()).containsExactly(fgUser, user2, user3, user4);
}
@Test
public void testGetAllUsers_notHeadless() {
ShadowUserManager.setIsHeadlessSystemUserMode(false);
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two admin, and two non-admin users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createNonAdminUser(fgUserId);
UserInfo user2 = createAdminUser(fgUserId + 1);
UserInfo user3 = createNonAdminUser(fgUserId + 2);
UserInfo user4 = createAdminUser(fgUserId + 3);
mockGetUsers(systemUser, fgUser, user2, user3, user4);
// Should return all users
assertThat(mProfileHelper.getAllProfiles())
.containsExactly(systemUser, fgUser, user2, user3, user4);
}
@Test
public void testGetAllSwitchableUsers() {
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two non-foreground users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createAdminUser(fgUserId);
UserInfo user1 = createAdminUser(fgUserId + 1);
UserInfo user2 = createAdminUser(fgUserId + 2);
mockGetUsers(systemUser, fgUser, user1, user2);
// Should return all non-foreground users.
assertThat(mProfileHelper.getAllSwitchableProfiles()).containsExactly(user1, user2);
}
@Test
public void testGetAllSwitchableUsers_notHeadless() {
ShadowUserManager.setIsHeadlessSystemUserMode(false);
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two non-foreground users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createAdminUser(fgUserId);
UserInfo user1 = createAdminUser(fgUserId + 1);
UserInfo user2 = createAdminUser(fgUserId + 2);
mockGetUsers(systemUser, fgUser, user1, user2);
// Should return all non-foreground users.
assertThat(mProfileHelper.getAllSwitchableProfiles()).containsExactly(systemUser, user1,
user2);
}
@Test
public void testGetAllPersistentUsers() {
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two non-ephemeral users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createAdminUser(fgUserId);
UserInfo user2 = createAdminUser(fgUserId + 1);
// Create two ephemeral users.
UserInfo user3 = createEphemeralUser(fgUserId + 2);
UserInfo user4 = createEphemeralUser(fgUserId + 3);
mockGetUsers(systemUser, fgUser, user2, user3, user4);
// Should return all non-ephemeral users.
assertThat(mProfileHelper.getAllPersistentProfiles()).containsExactly(fgUser, user2);
}
@Test
public void testGetAllPersistentUsers_notHeadless() {
ShadowUserManager.setIsHeadlessSystemUserMode(false);
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two non-ephemeral users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createAdminUser(fgUserId);
UserInfo user2 = createAdminUser(fgUserId + 1);
// Create two ephemeral users.
UserInfo user3 = createEphemeralUser(fgUserId + 2);
UserInfo user4 = createEphemeralUser(fgUserId + 3);
mockGetUsers(systemUser, fgUser, user2, user3, user4);
// Should return all non-ephemeral users.
assertThat(mProfileHelper.getAllPersistentProfiles()).containsExactly(systemUser, fgUser,
user2);
}
@Test
public void testGetAllAdminUsers() {
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two admin, and two non-admin users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createNonAdminUser(fgUserId);
UserInfo user2 = createAdminUser(fgUserId + 1);
UserInfo user3 = createNonAdminUser(fgUserId + 2);
UserInfo user4 = createAdminUser(fgUserId + 3);
mockGetUsers(systemUser, fgUser, user2, user3, user4);
// Should return only admin users.
assertThat(mProfileHelper.getAllAdminProfiles()).containsExactly(user2, user4);
}
@Test
public void testGetAllAdminUsers_notHeadless() {
ShadowUserManager.setIsHeadlessSystemUserMode(false);
// Add system user
UserInfo systemUser = createAdminUser(UserHandle.USER_SYSTEM);
// Create two admin, and two non-admin users.
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createNonAdminUser(fgUserId);
UserInfo user2 = createAdminUser(fgUserId + 1);
UserInfo user3 = createNonAdminUser(fgUserId + 2);
UserInfo user4 = createAdminUser(fgUserId + 3);
mockGetUsers(systemUser, fgUser, user2, user3, user4);
// Should return only admin users.
assertThat(mProfileHelper.getAllAdminProfiles()).containsExactly(systemUser, user2, user4);
}
@Test
public void testRemoveUser_isAdminUser_cannotRemoveSystemUser() {
UserInfo systemUser = new UserInfo(
UserHandle.USER_SYSTEM,
"Driver",
/* iconPath= */ null,
/* flags= */ UserInfo.FLAG_ADMIN | UserInfo.FLAG_SYSTEM,
/* userType= */ USER_TYPE_SYSTEM_HEADLESS);
assertThat(mProfileHelper.removeProfile(mContext, systemUser))
.isEqualTo(ProfileHelper.REMOVE_PROFILE_RESULT_FAILED);
}
@Test
public void testRemoveUser_isAdmin_canRemoveOtherUsers() {
// Create admin user and non-admin user
int fgUserId = ActivityManager.getCurrentUser();
int nonAdminUserId = fgUserId + 1;
UserInfo fgUser = createAdminUser(fgUserId);
UserInfo nonAdminUser = createNonAdminUser(nonAdminUserId);
mockGetUsers(fgUser, nonAdminUser);
mockRemoveUser(nonAdminUserId, UserRemovalResult.STATUS_SUCCESSFUL);
// If Admin is removing non-current, non-system user, simply calls removeUser.
when(mMockUserManager.isAdminUser()).thenReturn(true);
assertThat(mProfileHelper.removeProfile(mContext, nonAdminUser))
.isEqualTo(ProfileHelper.REMOVE_PROFILE_RESULT_SUCCESS);
verify(mMockCarUserManager).removeUser(nonAdminUserId);
}
@Test
public void testRemoveUser_isNonAdmin_cannotRemoveOtherUsers() {
// Create two non-admin users
int fgUserId = ActivityManager.getCurrentUser();
UserInfo fgUser = createNonAdminUser(fgUserId);
UserInfo user2 = createNonAdminUser(fgUserId + 1);
mockGetUsers(fgUser, user2);
// Make current user non-admin.
when(mMockUserManager.isAdminUser()).thenReturn(false);
// Mock so that removeUser always pretends it's successful.
mockRemoveUserSuccess();
// If Non-Admin is trying to remove someone other than themselves, they should fail.
assertThat(mProfileHelper.removeProfile(mContext, user2))
.isEqualTo(ProfileHelper.REMOVE_PROFILE_RESULT_FAILED);
verify(mMockCarUserManager, never()).removeUser(user2.id);
}
@Test
public void testRemoveUser_removesLastAdminUser_createsAndSwitchesToNewAdminUser() {
// Ensure admin status
when(mMockUserManager.isAdminUser()).thenReturn(true);
// Create one admin and one non-admin
int baseId = 10;
UserInfo adminUser = createAdminUser(baseId);
UserInfo nonAdminInfo = createNonAdminUser(baseId + 1);
mockGetUsers(adminUser, nonAdminInfo);
UserInfo newAdminInfo = createAdminUser(baseId + 2);
mockRemoveUserSuccess();
mockCreateUser(DEFAULT_ADMIN_NAME, UserInfo.FLAG_ADMIN,
UserCreationResult.STATUS_SUCCESSFUL, newAdminInfo);
mockSwitchUserSuccess();
assertThat(mProfileHelper.removeProfile(mContext, adminUser))
.isEqualTo(ProfileHelper.REMOVE_PROFILE_RESULT_SUCCESS);
verify(mMockCarUserManager).createUser(DEFAULT_ADMIN_NAME, UserInfo.FLAG_ADMIN);
verify(mMockCarUserManager).switchUser(newAdminInfo.id);
verify(mMockCarUserManager).removeUser(adminUser.id);
}
@Test
public void testRemoveUser_removesLastAdminUserFailsCreateNewUser_doesNotRemoveOrSwitchUser() {
// Ensure admin status
when(mMockUserManager.isAdminUser()).thenReturn(true);
// Create one admin and one non-admin
int baseId = 10;
UserInfo adminUser = createAdminUser(baseId);
UserInfo nonAdminInfo = createNonAdminUser(baseId + 1);
mockGetUsers(adminUser, nonAdminInfo);
// Fail to create a new user to force a failure case
mockCreateUser(DEFAULT_ADMIN_NAME, UserInfo.FLAG_ADMIN,
UserCreationResult.STATUS_ANDROID_FAILURE, null);
assertThat(mProfileHelper.removeProfile(mContext, adminUser))
.isEqualTo(ProfileHelper.REMOVE_PROFILE_RESULT_FAILED);
verify(mMockCarUserManager).createUser(DEFAULT_ADMIN_NAME, UserInfo.FLAG_ADMIN);
verify(mMockCarUserManager, never()).switchUser(anyInt());
verify(mMockCarUserManager, never()).removeUser(adminUser.id);
}
@Test
public void testRemoveUser_removeForegroundUser_callsSwitchToGuest() {
// Create foreground user
int baseId = 10;
ShadowProcess.setUid(baseId * UserHandle.PER_USER_RANGE); // User 10
UserInfo currentUser = createNonAdminUser(baseId);
when(mMockUserManager.isAdminUser()).thenReturn(false);
mockGetUsers(currentUser);
UserInfo guestUser = createGuestUser(baseId + 1);
mockRemoveUserSuccess();
mockCreateGuest(DEFAULT_GUEST_NAME, UserCreationResult.STATUS_SUCCESSFUL, guestUser);
mockSwitchUserSuccess();
assertUserRemoved(ProfileHelper.REMOVE_PROFILE_RESULT_SUCCESS, guestUser, currentUser);
}
@Test
public void testRemoveUser_removeForegroundUser_switchFails_returnsSetEphemeralResult() {
// Create foreground user
int baseId = 10;
ShadowProcess.setUid(baseId * UserHandle.PER_USER_RANGE); // User 10
UserInfo currentUser = createNonAdminUser(baseId);
when(mMockUserManager.isAdminUser()).thenReturn(false);
mockGetUsers(currentUser);
UserInfo guestUser = createGuestUser(baseId + 1);
mockRemoveUserSuccess();
mockCreateGuest(DEFAULT_GUEST_NAME, UserCreationResult.STATUS_SUCCESSFUL, guestUser);
mockSwitchUserFailure();
assertUserRemoved(ProfileHelper.REMOVE_PROFILE_RESULT_SWITCH_FAILED, guestUser,
currentUser);
}
private void assertUserRemoved(int expectedResult, UserInfo newUser, UserInfo removedUser) {
assertThat(mProfileHelper.removeProfile(mContext, removedUser)).isEqualTo(expectedResult);
verify(mMockCarUserManager).createGuest(newUser.name);
verify(mMockCarUserManager).switchUser(newUser.id);
verify(mMockCarUserManager).removeUser(removedUser.id);
}
@Test
public void testGetMaxSupportedRealUsers_isHeadless() {
ShadowUserManager.setIsHeadlessSystemUserMode(true);
when(mMockUserManager.getMaxSupportedUsers()).thenReturn(7);
// Create System user, two managed profiles, and two normal users.
UserInfo user0 = createAdminUser(0);
UserInfo user1 = createNonAdminUser(10);
UserInfo user2 = createManagedProfile(11);
UserInfo user3 = createNonAdminUser(13);
UserInfo user4 = createManagedProfile(14);
mockGetUsers(user0, user1, user2, user3, user4);
// Max users - # managed profiles - headless system user.
assertThat(mProfileHelper.getMaxSupportedRealProfiles()).isEqualTo(4);
}
@Test
public void testGetMaxSupportedRealUsers_isNotHeadless() {
ShadowUserManager.setIsHeadlessSystemUserMode(false);
when(mMockUserManager.getMaxSupportedUsers()).thenReturn(7);
// Create System user, two managed profiles, and two normal users.
UserInfo user0 = createAdminUser(0);
UserInfo user1 = createNonAdminUser(10);
UserInfo user2 = createManagedProfile(11);
UserInfo user3 = createNonAdminUser(13);
UserInfo user4 = createManagedProfile(14);
mockGetUsers(user0, user1, user2, user3, user4);
// Max users - # managed profiles
assertThat(mProfileHelper.getMaxSupportedRealProfiles()).isEqualTo(5);
}
@Test
public void testCreateNewOrFindExistingGuest_ifGuestExists_returnsExistingGuest() {
// Create two users and a guest user.
UserInfo user1 = createAdminUser(10);
UserInfo user2 = createNonAdminUser(12);
UserInfo guestUser = createGuestUser(13);
mockGetUsers(user1, user2, guestUser);
mockCreateUserFail();
when(mMockUserManager.findCurrentGuestUser()).thenReturn(guestUser);
UserInfo guest = mProfileHelper.createNewOrFindExistingGuest(mContext);
assertThat(guest).isEqualTo(guestUser);
}
@Test
public void testCreateNewOrFindExistingGuest_ifNoGuest_createsNewGuest() {
// Create two users.
UserInfo user1 = createAdminUser(10);
UserInfo user2 = createNonAdminUser(12);
mockGetUsers(user1, user2);
// Create a user for the "new guest" user.
UserInfo guestInfo = createGuestUser(21);
mockCreateGuest(DEFAULT_GUEST_NAME, UserCreationResult.STATUS_SUCCESSFUL, guestInfo);
UserInfo guest = mProfileHelper.createNewOrFindExistingGuest(mContext);
verify(mMockCarUserManager).createGuest(DEFAULT_GUEST_NAME);
assertThat(guest).isEqualTo(guestInfo);
}
private UserInfo createAdminUser(int id) {
return new UserInfo(id, null, UserInfo.FLAG_ADMIN);
}
private UserInfo createNonAdminUser(int id) {
return new UserInfo(id, null, 0);
}
private UserInfo createEphemeralUser(int id) {
return new UserInfo(id, null, UserInfo.FLAG_EPHEMERAL);
}
private UserInfo createManagedProfile(int id) {
return new UserInfo(id, null, UserInfo.FLAG_MANAGED_PROFILE);
}
private UserInfo createGuestUser(int id) {
return new UserInfo(id, null, UserInfo.FLAG_GUEST);
}
private void mockGetUsers(UserInfo... users) {
mockUmGetAliveUsers(mMockUserManager, users);
}
private void mockRemoveUser(int userId, int status) {
when(mMockCarUserManager.removeUser(userId)).thenReturn(new UserRemovalResult(status));
}
private void mockRemoveUserSuccess() {
when(mMockCarUserManager.removeUser(anyInt()))
.thenReturn(new UserRemovalResult(UserRemovalResult.STATUS_SUCCESSFUL));
}
private void mockCreateUserFail() {
AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
future.complete(new UserCreationResult(UserCreationResult.STATUS_ANDROID_FAILURE,
/* user= */ null));
AndroidAsyncFuture<UserCreationResult> asyncFuture = new AndroidAsyncFuture<>(future);
when(mMockCarUserManager.createUser(any(), anyInt())).thenReturn(asyncFuture);
when(mMockCarUserManager.createGuest(any())).thenReturn(asyncFuture);
}
private void mockCreateUser(String name, int flag, int status, UserInfo userInfo) {
AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
future.complete(new UserCreationResult(status, userInfo.getUserHandle()));
when(mMockCarUserManager.createUser(name, flag))
.thenReturn(new AndroidAsyncFuture<>(future));
}
private void mockCreateGuest(String name, int status, UserInfo userInfo) {
AndroidFuture<UserCreationResult> future = new AndroidFuture<>();
future.complete(new UserCreationResult(status, userInfo.getUserHandle()));
when(mMockCarUserManager.createGuest(name)).thenReturn(new AndroidAsyncFuture<>(future));
}
private void mockSwitchUserSuccess() {
AndroidFuture<UserSwitchResult> future = new AndroidFuture<>();
future.complete(
new UserSwitchResult(UserSwitchResult.STATUS_SUCCESSFUL, /* errorMessage= */null));
when(mMockCarUserManager.switchUser(anyInt())).thenReturn(new AndroidAsyncFuture<>(future));
}
private void mockSwitchUserFailure() {
AndroidFuture<UserSwitchResult> future = new AndroidFuture<>();
future.complete(new UserSwitchResult(UserSwitchResult.STATUS_ANDROID_FAILURE,
/* errorMessage= */null));
when(mMockCarUserManager.switchUser(anyInt())).thenReturn(new AndroidAsyncFuture<>(future));
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2019 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.car.settings.profiles;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
import com.android.car.settings.testutils.ShadowUserManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class})
public class ProfileIconProviderTest {
private Context mContext;
private ProfileIconProvider mProfileIconProvider;
private UserInfo mUserInfo;
private UserManager mUserManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mProfileIconProvider = new ProfileIconProvider();
mUserInfo = new UserInfo(/* id= */ 10, "USER_NAME", /* flags= */ 0);
}
@After
public void tearDown() {
ShadowUserManager.reset();
}
@Test
public void getRoundedUserIcon_AssignsIconIfNotPresent() {
ShadowUserManager.setUserIcon(mUserInfo.id, null);
Drawable returnedIcon = mProfileIconProvider.getRoundedProfileIcon(mUserInfo, mContext);
assertThat(returnedIcon).isNotNull();
assertThat(getShadowUserManager().getUserIcon(mUserInfo.id)).isNotNull();
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(mUserManager);
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2018 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.car.settings.profiles;
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.content.Context;
import android.content.pm.UserInfo;
import androidx.preference.Preference;
import com.android.car.settings.R;
import com.android.car.settings.testutils.ShadowUserHelper;
import com.android.car.settings.testutils.ShadowUserIconProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserIconProvider.class, ShadowUserHelper.class})
public class ProfilesPreferenceProviderTest {
private static final String TEST_CURRENT_USER_NAME = "Current User";
private static final String TEST_OTHER_USER_1_NAME = "User 1";
private static final String TEST_OTHER_USER_2_NAME = "User 2";
private static final String TEST_GUEST_USER_1_NAME = "Guest 1";
private static final String TEST_GUEST_USER_2_NAME = "Guest 2";
private static final UserInfo TEST_CURRENT_USER = new UserInfo(/* id= */ 14,
TEST_CURRENT_USER_NAME, /* flags= */ 0);
private static final UserInfo TEST_OTHER_USER_1 = new UserInfo(/* id= */ 10,
TEST_OTHER_USER_1_NAME, /* flags= */ 0);
private static final UserInfo TEST_OTHER_USER_2 = new UserInfo(/* id= */ 11,
TEST_OTHER_USER_2_NAME, /* flags= */ 0);
private static final UserInfo TEST_GUEST_USER_1 = new UserInfo(/* id= */ 12,
TEST_GUEST_USER_1_NAME, /* flags= */ UserInfo.FLAG_GUEST);
private static final UserInfo TEST_GUEST_USER_2 = new UserInfo(/* id= */ 13,
TEST_GUEST_USER_2_NAME, /* flags= */ UserInfo.FLAG_GUEST);
private Context mContext;
@Mock
private ProfilesPreferenceProvider.ProfileClickListener mProfileClickListener;
@Mock
private ProfileHelper mProfileHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowUserHelper.setInstance(mProfileHelper);
mContext = RuntimeEnvironment.application;
List<UserInfo> users = Arrays.asList(TEST_OTHER_USER_1, TEST_GUEST_USER_1,
TEST_GUEST_USER_2,
TEST_OTHER_USER_2);
when(mProfileHelper.getCurrentProcessUserInfo()).thenReturn(TEST_CURRENT_USER);
when(mProfileHelper.isCurrentProcessUser(TEST_CURRENT_USER)).thenReturn(true);
when(mProfileHelper.getAllSwitchableProfiles()).thenReturn(users);
when(mProfileHelper.getAllLivingProfiles(any())).thenReturn(
Arrays.asList(TEST_OTHER_USER_1, TEST_OTHER_USER_2));
}
@After
public void tearDown() {
ShadowUserHelper.reset();
}
@Test
public void testCreateUserList_firstUserIsCurrentUser() {
ProfilesPreferenceProvider provider = createProvider();
Preference first = provider.createProfileList().get(0);
assertThat(first.getTitle()).isEqualTo(
mContext.getString(R.string.current_user_name, TEST_CURRENT_USER_NAME));
}
@Test
public void testCreateUserList_repeatedGuestUserNotShown() {
ProfilesPreferenceProvider provider = createProvider();
List<Preference> userList = provider.createProfileList();
assertThat(userList.size()).isEqualTo(4); // 3 real users + guest item.
assertThat(userList.get(0).getTitle()).isEqualTo(
mContext.getString(R.string.current_user_name, TEST_CURRENT_USER_NAME));
assertThat(userList.get(1).getTitle()).isEqualTo(TEST_OTHER_USER_1_NAME);
assertThat(userList.get(2).getTitle()).isEqualTo(TEST_OTHER_USER_2_NAME);
}
@Test
public void testCreateUserList_guestShownAsSeparateLastElement() {
ProfilesPreferenceProvider provider = createProvider();
List<Preference> userList = provider.createProfileList();
assertThat(userList.get(userList.size() - 1).getTitle()).isEqualTo(
mContext.getString(com.android.internal.R.string.guest_name));
}
@Test
public void testCreateUserList_currentUserNotShown() {
ProfilesPreferenceProvider provider = createProvider();
provider.setIncludeCurrentProfile(false);
List<Preference> userList = provider.createProfileList();
assertThat(userList.size()).isEqualTo(3); // 3 real users + guest item.
assertThat(userList.get(0).getTitle()).isEqualTo(TEST_OTHER_USER_1_NAME);
assertThat(userList.get(1).getTitle()).isEqualTo(TEST_OTHER_USER_2_NAME);
assertThat(userList.get(2).getTitle()).isEqualTo(
mContext.getString(com.android.internal.R.string.guest_name));
}
@Test
public void testCreateUserList_guestNotShown() {
ProfilesPreferenceProvider provider = createProvider();
provider.setIncludeGuest(false);
List<Preference> userList = provider.createProfileList();
assertThat(userList.size()).isEqualTo(3); // 3 real users.
assertThat(userList.get(0).getTitle()).isEqualTo(
mContext.getString(R.string.current_user_name, TEST_CURRENT_USER_NAME));
assertThat(userList.get(1).getTitle()).isEqualTo(TEST_OTHER_USER_1_NAME);
assertThat(userList.get(2).getTitle()).isEqualTo(TEST_OTHER_USER_2_NAME);
}
@Test
public void testPerformClick_currentUser_invokesUserClickListener() {
ProfilesPreferenceProvider provider = createProvider();
List<Preference> userList = provider.createProfileList();
userList.get(0).performClick();
verify(mProfileClickListener).onProfileClicked(TEST_CURRENT_USER);
}
@Test
public void testPerformClick_otherUser_invokesUserClickListener() {
ProfilesPreferenceProvider provider = createProvider();
List<Preference> userList = provider.createProfileList();
userList.get(1).performClick();
verify(mProfileClickListener).onProfileClicked(TEST_OTHER_USER_1);
}
@Test
public void testPerformClick_guestUser_doesntInvokeUserClickListener() {
ProfilesPreferenceProvider provider = createProvider();
List<Preference> userList = provider.createProfileList();
userList.get(userList.size() - 1).performClick();
verify(mProfileClickListener, never()).onProfileClicked(any(UserInfo.class));
}
private ProfilesPreferenceProvider createProvider() {
return new ProfilesPreferenceProvider(mContext, mProfileClickListener);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2019 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.car.settings.security;
import static android.os.UserManager.DISALLOW_CONFIG_CREDENTIALS;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.preference.Preference;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowUserManager;
/** Unit test for {@link CredentialsResetPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
public class CredentialsResetPreferenceControllerTest {
private PreferenceControllerTestHelper<CredentialsResetPreferenceController> mControllerHelper;
private UserHandle mMyUserHandle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mMyUserHandle = UserHandle.of(UserHandle.myUserId());
Context context = RuntimeEnvironment.application;
mControllerHelper = new PreferenceControllerTestHelper<>(context,
CredentialsResetPreferenceController.class, new Preference(context));
}
@Test
public void getAvailabilityStatus_noRestrictions_returnsAvailable() {
getShadowUserManager().setUserRestriction(
mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, false);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_noRestrictions_returnsAvailable_zoneWrite() {
mControllerHelper.getController().setAvailabilityStatusForZone("write");
getShadowUserManager().setUserRestriction(
mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, false);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_noRestrictions_returnsAvailable_zoneRead() {
mControllerHelper.getController().setAvailabilityStatusForZone("read");
getShadowUserManager().setUserRestriction(
mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, false);
assertThat(mControllerHelper.getController().getAvailabilityStatus())
.isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_noRestrictions_returnsAvailable_zoneHidden() {
mControllerHelper.getController().setAvailabilityStatusForZone("hidden");
getShadowUserManager().setUserRestriction(
mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, false);
assertThat(mControllerHelper.getController().getAvailabilityStatus())
.isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_userRestricted_returnsDisabledForUser() {
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, true);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_userRestricted_returnsDisabledForUser_zoneWrite() {
mControllerHelper.getController().setAvailabilityStatusForZone("write");
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, true);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_userRestricted_returnsDisabledForUser_zoneRead() {
mControllerHelper.getController().setAvailabilityStatusForZone("read");
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, true);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_userRestricted_returnsDisabledForUser_zoneHidden() {
mControllerHelper.getController().setAvailabilityStatusForZone("hidden");
getShadowUserManager().setUserRestriction(mMyUserHandle, DISALLOW_CONFIG_CREDENTIALS, true);
assertThat(mControllerHelper.getController().getAvailabilityStatus()).isEqualTo(
DISABLED_FOR_PROFILE);
}
private ShadowUserManager getShadowUserManager() {
return Shadow.extract(UserManager.get(RuntimeEnvironment.application));
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2019 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.car.settings.security;
import static com.google.common.truth.Truth.assertThat;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.os.RemoteException;
import com.android.car.settings.setupservice.InitialLockSetupService;
import com.android.car.settings.testutils.ShadowLockPatternUtils;
import com.android.car.setupwizardlib.IInitialLockSetupService;
import com.android.car.setupwizardlib.InitialLockSetupConstants.LockTypes;
import com.android.car.setupwizardlib.InitialLockSetupConstants.SetLockCodes;
import com.android.car.setupwizardlib.InitialLockSetupConstants.ValidateLockFlags;
import com.android.car.setupwizardlib.InitialLockSetupHelper;
import com.android.car.setupwizardlib.LockConfig;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContextWrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Tests that the {@link InitialLockSetupService} properly handles connections and lock requests.
*/
@Config(shadows = ShadowLockPatternUtils.class)
@RunWith(RobolectricTestRunner.class)
public class InitialLockSetupServiceTest {
private static final String LOCK_PERMISSION = "com.android.car.settings.SET_INITIAL_LOCK";
private InitialLockSetupService mInitialLockSetupService;
private Context mContext;
@Before
public void setupService() {
ShadowLockPatternUtils.reset();
mInitialLockSetupService = Robolectric.buildService(InitialLockSetupService.class)
.create()
.get();
mContext = RuntimeEnvironment.application;
ShadowContextWrapper shadowContextWrapper = Shadows.shadowOf((ContextWrapper) mContext);
shadowContextWrapper.grantPermissions(LOCK_PERMISSION);
}
@Test
public void testBindReturnsNull_ifLockSet() {
ShadowLockPatternUtils.setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
assertThat(mInitialLockSetupService.onBind(new Intent())).isNull();
}
@Test
public void testBindReturnsInstanceOfServiceInterface_ifLockNotSet() throws RemoteException {
assertThat(mInitialLockSetupService.onBind(
new Intent()) instanceof IInitialLockSetupService.Stub).isTrue();
}
@Test
public void testGetLockConfig_returnsCorrectConfig() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
LockConfig pinConfig = service.getLockConfig(LockTypes.PIN);
assertThat(pinConfig.enabled).isTrue();
assertThat(pinConfig.minLockLength).isEqualTo(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
LockConfig patternConfig = service.getLockConfig(LockTypes.PATTERN);
assertThat(patternConfig.enabled).isTrue();
assertThat(patternConfig.minLockLength).isEqualTo(LockPatternUtils.MIN_LOCK_PATTERN_SIZE);
LockConfig passwordConfig = service.getLockConfig(LockTypes.PASSWORD);
assertThat(passwordConfig.enabled).isTrue();
assertThat(passwordConfig.minLockLength).isEqualTo(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
}
@Test
public void testCheckValidLock_tooShort() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
int result = service.checkValidLock(LockTypes.PASSWORD, "hi".getBytes());
assertThat(result & ValidateLockFlags.INVALID_LENGTH)
.isEqualTo(ValidateLockFlags.INVALID_LENGTH);
}
@Test
public void testCheckValidLock_longEnough() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
int result = service.checkValidLock(LockTypes.PASSWORD, "password".getBytes());
assertThat(result & ValidateLockFlags.INVALID_LENGTH)
.isNotEqualTo(ValidateLockFlags.INVALID_LENGTH);
}
@Test
public void testCheckValidLockPin_withLetters() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
int result = service.checkValidLock(LockTypes.PIN, "12a3".getBytes());
assertThat(result & ValidateLockFlags.INVALID_BAD_SYMBOLS)
.isEqualTo(ValidateLockFlags.INVALID_BAD_SYMBOLS);
}
@Test
public void testCheckValidLockPattern_tooShort() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
byte[] pattern = new byte[LockPatternUtils.MIN_LOCK_PATTERN_SIZE - 1];
for (int i = 0; i < pattern.length; i++) {
pattern[i] = (byte) i;
}
int result = service.checkValidLock(LockTypes.PATTERN, pattern);
assertThat(result & ValidateLockFlags.INVALID_LENGTH)
.isEqualTo(ValidateLockFlags.INVALID_LENGTH);
}
@Test
public void testCheckValidLockPattern_longEnough() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
byte[] pattern = new byte[LockPatternUtils.MIN_LOCK_PATTERN_SIZE + 1];
for (int i = 0; i < pattern.length; i++) {
pattern[i] = (byte) i;
}
int result = service.checkValidLock(LockTypes.PATTERN, pattern);
assertThat(result & ValidateLockFlags.INVALID_LENGTH)
.isNotEqualTo(ValidateLockFlags.INVALID_LENGTH);
}
@Test
public void testSetLockPassword_doesNotWorkWithExistingPassword() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
ShadowLockPatternUtils.setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
int result = service.setLock(LockTypes.PASSWORD, "password".getBytes());
assertThat(result).isEqualTo(SetLockCodes.FAIL_LOCK_EXISTS);
}
@Test
public void testSetLockPassword_doesNotWorkWithInvalidPassword() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
int result = service.setLock(LockTypes.PASSWORD, "hi".getBytes());
assertThat(result).isEqualTo(SetLockCodes.FAIL_LOCK_INVALID);
}
@Test
public void testSetLockPassword_setsDevicePassword() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
byte[] password = "password".getBytes();
// Need copy since password is cleared.
byte[] expectedPassword = Arrays.copyOf(password, password.length);
int result = service.setLock(LockTypes.PASSWORD, password);
assertThat(result).isEqualTo(SetLockCodes.SUCCESS);
assertThat(Arrays.equals(ShadowLockPatternUtils.getSavedPassword(),
expectedPassword)).isTrue();
}
@Test
public void testSetLockPin_setsDevicePin() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
byte[] password = "1580".getBytes();
byte[] expectedPassword = Arrays.copyOf(password, password.length);
int result = service.setLock(LockTypes.PIN, password);
assertThat(result).isEqualTo(SetLockCodes.SUCCESS);
assertThat(Arrays.equals(ShadowLockPatternUtils.getSavedPassword(),
expectedPassword)).isTrue();
}
@Test
public void testSetLockPattern_setsDevicePattern() throws RemoteException {
IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
mInitialLockSetupService.onBind(new Intent()));
List<LockPatternView.Cell> pattern = new ArrayList<>();
pattern.add(LockPatternView.Cell.of(0, 0));
pattern.add(LockPatternView.Cell.of(1, 0));
pattern.add(LockPatternView.Cell.of(2, 0));
pattern.add(LockPatternView.Cell.of(0, 1));
byte[] patternBytes = new byte[pattern.size()];
for (int i = 0; i < patternBytes.length; i++) {
LockPatternView.Cell cell = pattern.get(i);
patternBytes[i] = InitialLockSetupHelper.getByteFromPatternCell(cell.getRow(),
cell.getColumn());
}
int result = service.setLock(LockTypes.PATTERN, patternBytes);
assertThat(result).isEqualTo(SetLockCodes.SUCCESS);
byte[] savedPattern = ShadowLockPatternUtils.getSavedPattern();
assertThat(savedPattern).isEqualTo(LockPatternUtils.patternToByteArray(pattern));
}
@Test
public void testBindFails_ifNoPermissionGranted() {
ShadowContextWrapper shadowContextWrapper = Shadows.shadowOf((ContextWrapper) mContext);
shadowContextWrapper.denyPermissions(LOCK_PERMISSION);
assertThat(mInitialLockSetupService.onBind(new Intent())).isNull();
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2018 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.car.settings.security;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.car.settings.common.ConfirmationDialogFragment;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowLockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowLockPatternUtils.class})
public class NoLockPreferenceControllerTest {
private static final LockscreenCredential TEST_CURRENT_PASSWORD =
LockscreenCredential.createPassword("test_password");
private Context mContext;
private PreferenceControllerTestHelper<NoLockPreferenceController> mPreferenceControllerHelper;
private NoLockPreferenceController mController;
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreferenceControllerHelper = new PreferenceControllerTestHelper<>(mContext,
NoLockPreferenceController.class, mPreference);
mController = mPreferenceControllerHelper.getController();
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
@After
public void tearDown() {
ShadowLockPatternUtils.reset();
}
@Test
public void testHandlePreferenceClicked_returnsTrue() {
assertThat(mController.handlePreferenceClicked(mPreference)).isTrue();
}
@Test
public void testHandlePreferenceClicked_ifNotSelectedAsLock_goesToNextFragment() {
mController.setCurrentPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
mPreference.performClick();
verify(mPreferenceControllerHelper.getMockFragmentController()).showDialog(
any(ConfirmationDialogFragment.class), anyString());
}
@Test
public void testHandlePreferenceClicked_ifSelectedAsLock_goesBack() {
mController.setCurrentPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
mPreference.performClick();
verify(mPreferenceControllerHelper.getMockFragmentController()).goBack();
}
@Test
public void testConfirmRemoveScreenLockListener_removesLock() {
mController.setCurrentPassword(TEST_CURRENT_PASSWORD);
mController.mConfirmListener.onConfirm(/* arguments= */ null);
assertThat(ShadowLockPatternUtils.getClearLockCredential()).isEqualTo(
TEST_CURRENT_PASSWORD);
assertThat(ShadowLockPatternUtils.getClearLockUser()).isEqualTo(UserHandle.myUserId());
}
@Test
public void testConfirmRemoveScreenLockListener_goesBack() {
mController.setCurrentPassword(TEST_CURRENT_PASSWORD);
mController.mConfirmListener.onConfirm(/* arguments= */ null);
verify(mPreferenceControllerHelper.getMockFragmentController()).goBack();
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2018 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.car.settings.security;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
import android.view.View;
import com.android.car.settings.R;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class PinPadViewTest {
private static int[] sAllKeys =
Arrays.copyOf(PinPadView.PIN_PAD_DIGIT_KEYS, PinPadView.NUM_KEYS);
static {
sAllKeys[PinPadView.PIN_PAD_DIGIT_KEYS.length] = R.id.key_backspace;
sAllKeys[PinPadView.PIN_PAD_DIGIT_KEYS.length + 1] = R.id.key_enter;
}
private PinPadView mPinPadView;
@Mock
private PinPadView.PinPadClickListener mClickListener;
@Before
public void initPinPad() {
initMocks(this);
mPinPadView = new PinPadView(RuntimeEnvironment.application);
mPinPadView.setPinPadClickListener(mClickListener);
}
// Verify that when the pin pad is enabled or disabled, all the keys are in the same state.
@Test
public void testEnableDisablePinPad() {
mPinPadView.setEnabled(false);
for (int id : sAllKeys) {
View key = mPinPadView.findViewById(id);
assertThat(key.isEnabled()).isFalse();
}
mPinPadView.setEnabled(true);
for (int id : sAllKeys) {
View key = mPinPadView.findViewById(id);
assertThat(key.isEnabled()).isTrue();
}
}
// Verify that the click handler is called when the backspace key is clicked.
@Test
@Ignore
public void testBackspaceClickHandler() {
mPinPadView.findViewById(R.id.key_backspace).performClick();
verify(mClickListener).onBackspaceClick();
}
// Verify that the click handler is called when the enter key is clicked.
@Test
public void testEnterKeyClickHandler() {
mPinPadView.findViewById(R.id.key_enter).performClick();
verify(mClickListener).onEnterKeyClick();
}
// Verify that the click handler is called with the right argument when a digit key is clicked.
@Test
public void testDigitKeyClickHandler() {
for (int i = 0; i < PinPadView.PIN_PAD_DIGIT_KEYS.length; ++i) {
mPinPadView.findViewById(PinPadView.PIN_PAD_DIGIT_KEYS[i]).performClick();
verify(mClickListener).onDigitKeyClick(String.valueOf(i));
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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.car.settings.security;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Tests for SaveLockWorker class.
*/
@RunWith(RobolectricTestRunner.class)
public class SaveLockWorkerTest {
/**
* A test to check return value when save worker succeeds
*/
@Test
public void testSaveLockSuccessReturnsTrue() {
SaveLockWorker worker = spy(new SaveLockWorker());
doNothing().when(worker).saveLock();
assertThat(worker.saveAndVerifyInBackground()).isTrue();
}
/**
* A test to check return value when save worker fails
*/
@Test
public void testSaveLockFailureReturnsFalse() {
SaveLockWorker worker = spy(new SaveLockWorker());
doThrow(new RuntimeException()).when(worker).saveLock();
assertThat(worker.saveAndVerifyInBackground()).isFalse();
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2018 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.car.settings.security;
import static com.android.car.settings.common.PreferenceController.AVAILABLE;
import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING;
import static com.android.car.settings.common.PreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.car.settings.common.PreferenceController.DISABLED_FOR_PROFILE;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowUserManager;
/** Unit test for {@link SecurityEntryPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
public class SecurityEntryPreferenceControllerTest {
private SecurityEntryPreferenceController mController;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new PreferenceControllerTestHelper<>(RuntimeEnvironment.application,
SecurityEntryPreferenceController.class).getController();
}
@Test
public void getAvailabilityStatus_guestUser_disabledForUser() {
getShadowUserManager().addUser(UserHandle.myUserId(), "name", UserInfo.FLAG_GUEST);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_guestUser_disabledForUser_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
getShadowUserManager().addUser(UserHandle.myUserId(), "name", UserInfo.FLAG_GUEST);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_guestUser_disabledForUser_zoneRead() {
mController.setAvailabilityStatusForZone("read");
getShadowUserManager().addUser(UserHandle.myUserId(), "name", UserInfo.FLAG_GUEST);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_guestUser_disabledForUser_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
getShadowUserManager().addUser(UserHandle.myUserId(), "name", UserInfo.FLAG_GUEST);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_PROFILE);
}
@Test
public void getAvailabilityStatus_nonGuestUser_available() {
getShadowUserManager().addUser(UserHandle.myUserId(), "name", /* flags= */ 0);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_nonGuestUser_available_zoneWrite() {
mController.setAvailabilityStatusForZone("write");
getShadowUserManager().addUser(UserHandle.myUserId(), "name", /* flags= */ 0);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_nonGuestUser_available_zoneRead() {
mController.setAvailabilityStatusForZone("read");
getShadowUserManager().addUser(UserHandle.myUserId(), "name", /* flags= */ 0);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_FOR_VIEWING);
}
@Test
public void getAvailabilityStatus_nonGuestUser_available_zoneHidden() {
mController.setAvailabilityStatusForZone("hidden");
getShadowUserManager().addUser(UserHandle.myUserId(), "name", /* flags= */ 0);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
private ShadowUserManager getShadowUserManager() {
return Shadows.shadowOf(UserManager.get(mContext));
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2018 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.car.settings.sound;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import androidx.lifecycle.Lifecycle;
import com.android.car.settings.common.ActivityResultCallback;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.car.settings.testutils.ShadowRingtone;
import com.android.car.settings.testutils.ShadowRingtoneManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowRingtoneManager.class, ShadowRingtone.class})
public class RingtonePreferenceControllerTest {
private static final int TEST_RINGTONE_TYPE = RingtoneManager.TYPE_RINGTONE;
private static final String TEST_PATH = "/test/path/uri";
private static final Uri TEST_URI = new Uri.Builder().appendPath(TEST_PATH).build();
private static final String TEST_TITLE = "Test Preference Title";
private static final String TEST_RINGTONE_TITLE = "Test Ringtone Title";
// These are copied from android.app.Activity. That class is not accessible from this test
// because there is another test Activity with the same package.
private static final int ACTIVITY_RESULT_OK = -1;
private static final int ACTIVITY_RESULT_CANCELLED = 0;
private Context mContext;
private PreferenceControllerTestHelper<RingtonePreferenceController>
mPreferenceControllerHelper;
private RingtonePreferenceController mController;
private RingtonePreference mRingtonePreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mRingtonePreference = new RingtonePreference(mContext, null);
mRingtonePreference.setTitle(TEST_TITLE);
mRingtonePreference.setRingtoneType(TEST_RINGTONE_TYPE);
mRingtonePreference.setShowSilent(true); // Default value when instantiated via xml.
mPreferenceControllerHelper = new PreferenceControllerTestHelper<>(mContext,
RingtonePreferenceController.class, mRingtonePreference);
mController = mPreferenceControllerHelper.getController();
mPreferenceControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_CREATE);
// Set the Uri to be null at the beginning of each test.
ShadowRingtoneManager.setActualDefaultRingtoneUri(mContext, TEST_RINGTONE_TYPE,
/* ringtoneUri= */ null);
}
@After
public void tearDown() {
ShadowRingtoneManager.reset();
ShadowRingtone.reset();
}
@Test
public void testRefreshUi_ringtoneTitleSet() {
ShadowRingtoneManager.setActualDefaultRingtoneUri(mContext, TEST_RINGTONE_TYPE, TEST_URI);
ShadowRingtone.setExpectedTitleForUri(TEST_URI, TEST_RINGTONE_TITLE);
mController.refreshUi();
assertThat(mRingtonePreference.getSummary()).isEqualTo(TEST_RINGTONE_TITLE);
}
@Test
public void testHandlePreferenceClicked_listenerTriggered() {
mRingtonePreference.performClick();
verify(mPreferenceControllerHelper.getMockFragmentController()).startActivityForResult(
any(Intent.class), anyInt(), any(ActivityResultCallback.class));
}
@Test
public void testHandlePreferenceClicked_captureIntent_checkDefaultUri() {
ShadowRingtoneManager.setActualDefaultRingtoneUri(mContext, TEST_RINGTONE_TYPE, TEST_URI);
mRingtonePreference.performClick();
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
verify(mPreferenceControllerHelper.getMockFragmentController()).startActivityForResult(
intent.capture(), anyInt(), any(ActivityResultCallback.class));
assertThat((Uri) intent.getValue().getParcelableExtra(
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI)).isEqualTo(TEST_URI);
}
@Test
public void testHandlePreferenceClicked_captureIntent_checkDialogTitle() {
mRingtonePreference.performClick();
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
verify(mPreferenceControllerHelper.getMockFragmentController()).startActivityForResult(
intent.capture(), anyInt(), any(ActivityResultCallback.class));
assertThat(
intent.getValue().getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE)).isEqualTo(
TEST_TITLE);
}
@Test
public void testHandlePreferenceClicked_captureIntent_checkRingtoneType() {
mRingtonePreference.performClick();
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
verify(mPreferenceControllerHelper.getMockFragmentController()).startActivityForResult(
intent.capture(), anyInt(), any(ActivityResultCallback.class));
assertThat(
intent.getValue().getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1)).isEqualTo(
TEST_RINGTONE_TYPE);
}
@Test
public void testHandlePreferenceClicked_captureIntent_checkShowSilent() {
mRingtonePreference.performClick();
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
verify(mPreferenceControllerHelper.getMockFragmentController()).startActivityForResult(
intent.capture(), anyInt(), any(ActivityResultCallback.class));
assertThat(intent.getValue().getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT,
false)).isTrue();
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2018 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.car.settings.sound;
import static android.media.AudioAttributes.USAGE_ALARM;
import static android.media.AudioAttributes.USAGE_MEDIA;
import static com.google.common.truth.Truth.assertThat;
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.content.Context;
import android.content.res.Resources;
import android.media.AudioAttributes.AttributeUsage;
import android.media.Ringtone;
import android.net.Uri;
import android.provider.Settings.System;
import com.android.car.settings.R;
import com.android.car.settings.testutils.ShadowRingtoneManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowRingtoneManager.class})
public class VolumeSettingsRingtoneManagerTest {
private static final int TEST_GROUP_ID = 1;
private static final int TEST_USAGE_ID = USAGE_MEDIA;
private Context mContext;
private VolumeSettingsRingtoneManager mRingtoneManager;
@Mock
private Ringtone mRingtone;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowRingtoneManager.setRingtone(mRingtone);
mContext = spy(RuntimeEnvironment.application);
mRingtoneManager = new VolumeSettingsRingtoneManager(mContext);
}
@After
public void tearDown() {
ShadowRingtoneManager.reset();
when(mRingtone.isPlaying()).thenReturn(false);
}
@Test
public void getRingtoneUri_defaultsToDefaultRingtoneUriIfConfigIsEmpty() {
setResources(new int[] {}, new String[] {});
mRingtoneManager = new VolumeSettingsRingtoneManager(mContext);
Uri ringtoneUri = mRingtoneManager.getRingtoneUri(USAGE_ALARM);
assertThat(ringtoneUri).isEqualTo(System.DEFAULT_RINGTONE_URI);
}
@Test
public void getRingtoneUri_defaultsToDefaultRingtoneUriIfUsageNotInConfig() {
setResources(
new int[] {USAGE_MEDIA},
new String[] {"content://settings/system/notification_sound"}
);
mRingtoneManager = new VolumeSettingsRingtoneManager(mContext);
Uri ringtoneUri = mRingtoneManager.getRingtoneUri(USAGE_ALARM);
assertThat(ringtoneUri).isEqualTo(System.DEFAULT_RINGTONE_URI);
}
@Test
public void getRingtoneUri_usesConfigValuesForUsageRingtoneMapping() {
@AttributeUsage int usage = USAGE_MEDIA;
String ringtoneValue = "content://settings/system/notification_sound";
setResources(new int[] {usage}, new String[] {ringtoneValue});
mRingtoneManager = new VolumeSettingsRingtoneManager(mContext);
Uri ringtoneUri = mRingtoneManager.getRingtoneUri(usage);
assertThat(ringtoneUri).isEqualTo(Uri.parse(ringtoneValue));
}
@Test
public void testPlayAudioFeedback_play_playUntilTimeout() {
mRingtoneManager.playAudioFeedback(TEST_GROUP_ID, TEST_USAGE_ID);
verify(mRingtone).play();
when(mRingtone.isPlaying()).thenReturn(true);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mRingtone).stop();
}
@Test
public void testPlayAudioFeedback_play_stoppedBeforeTimeout() {
mRingtoneManager.playAudioFeedback(TEST_GROUP_ID, TEST_USAGE_ID);
verify(mRingtone).play();
when(mRingtone.isPlaying()).thenReturn(false);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mRingtone, never()).stop();
}
@Test
public void testStopCurrentRingtone_stop() {
mRingtoneManager.playAudioFeedback(TEST_GROUP_ID, TEST_USAGE_ID);
mRingtoneManager.stopCurrentRingtone();
verify(mRingtone).stop();
}
@Test
public void testStopCurrentRingtone_noCurrentRingtone() {
mRingtoneManager.stopCurrentRingtone();
verify(mRingtone, never()).stop();
}
private void setResources(int[] ringtoneAudioAttributeUsages, String[] ringtoneUris) {
Resources resources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(resources);
when(resources.getIntArray(R.array.config_ringtone_audio_attribute_usages_map_key))
.thenReturn(ringtoneAudioAttributeUsages);
when(resources.getStringArray(R.array.config_ringtone_uri_map_value))
.thenReturn(ringtoneUris);
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2018 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.car.settings.suggestions;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.ShapeDrawable;
import android.service.settings.suggestions.Suggestion;
import android.view.View;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.preference.PreferenceViewHolder;
import com.android.car.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/** Unit test for {@link SuggestionPreference}. */
@RunWith(RobolectricTestRunner.class)
public class SuggestionPreferenceTest {
private static final String SUGGESTION_ID = "id";
@Mock
private SuggestionPreference.Callback mCallback;
private Suggestion mSuggestion;
private PreferenceViewHolder mHolder;
private SuggestionPreference mPref;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = RuntimeEnvironment.application;
mSuggestion = new Suggestion.Builder(SUGGESTION_ID).build();
Context themedContext = new ContextThemeWrapper(context, R.style.CarSettingTheme);
View rootView = View.inflate(themedContext, R.layout.suggestion_preference, null);
mHolder = PreferenceViewHolder.createInstanceForTests(rootView);
mPref = new SuggestionPreference(context, mSuggestion, mCallback);
}
@Test
public void getSuggestion_returnsSuggestion() {
assertThat(mPref.getSuggestion()).isEqualTo(mSuggestion);
}
@Test
public void updateSuggestion_updatesPreference() {
Icon icon = mock(Icon.class);
Drawable iconDrawable = new ShapeDrawable();
when(icon.loadDrawable(any(Context.class))).thenReturn(iconDrawable);
String title = "title";
String summary = "summary";
Suggestion updatedSuggestion = new Suggestion.Builder(SUGGESTION_ID).setIcon(icon).setTitle(
title).setSummary(summary).build();
mPref.updateSuggestion(updatedSuggestion);
assertThat(mPref.getSuggestion()).isEqualTo(updatedSuggestion);
assertThat(mPref.getIcon()).isEqualTo(iconDrawable);
assertThat(mPref.getTitle()).isEqualTo(title);
assertThat(mPref.getSummary()).isEqualTo(summary);
}
@Test
public void updateSuggestion_idMismatch_throwsIllegalArgumentException() {
Suggestion updatedSuggestion = new Suggestion.Builder(SUGGESTION_ID + "wrong id").build();
assertThrows(IllegalArgumentException.class,
() -> mPref.updateSuggestion(updatedSuggestion));
}
@Test
public void getKey_includesSuggestionId() {
assertThat(mPref.getKey()).isEqualTo(
SuggestionPreference.SUGGESTION_PREFERENCE_KEY + SUGGESTION_ID);
}
@Test
public void onClick_callsLaunchSuggestion() {
mPref.onBindViewHolder(mHolder);
mHolder.itemView.performClick();
verify(mCallback).launchSuggestion(mPref);
}
@Test
public void dismissButtonClick_callsDismissSuggestion() {
mPref.onBindViewHolder(mHolder);
mHolder.findViewById(R.id.dismiss_button).performClick();
verify(mCallback).dismissSuggestion(mPref);
}
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2018 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.car.settings.suggestions;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertThrows;
import android.app.PendingIntent;
import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import androidx.lifecycle.Lifecycle;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import com.android.car.settings.common.PreferenceControllerTestHelper;
import com.android.settingslib.suggestions.SuggestionController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** Unit test for {@link SuggestionsPreferenceController}. */
@RunWith(RobolectricTestRunner.class)
public class SuggestionsPreferenceControllerTest {
private static final Suggestion SUGGESTION_1 = new Suggestion.Builder("1").build();
private static final Suggestion SUGGESTION_2 = new Suggestion.Builder("2").build();
@Mock
private LoaderManager mLoaderManager;
@Mock
private Loader<List<Suggestion>> mLoader;
@Mock
private SuggestionController mSuggestionController;
private Context mContext;
private PreferenceGroup mGroup;
private PreferenceControllerTestHelper<SuggestionsPreferenceController> mControllerHelper;
private SuggestionsPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mGroup = new PreferenceCategory(mContext);
mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
SuggestionsPreferenceController.class);
mController = mControllerHelper.getController();
mController.setLoaderManager(mLoaderManager);
ReflectionHelpers.setField(SuggestionsPreferenceController.class, mController,
"mSuggestionController", mSuggestionController);
mControllerHelper.setPreference(mGroup);
mControllerHelper.markState(Lifecycle.State.STARTED);
}
@Test
public void setPreference_loaderManagerSet_doesNothing() {
PreferenceControllerTestHelper<SuggestionsPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
SuggestionsPreferenceController.class);
mController = helper.getController();
mController.setLoaderManager(mLoaderManager);
helper.setPreference(new PreferenceCategory(mContext));
}
@Test
public void checkInitialized_nullLoaderManager_throwsIllegalStateException() {
PreferenceControllerTestHelper<SuggestionsPreferenceController> helper =
new PreferenceControllerTestHelper<>(mContext,
SuggestionsPreferenceController.class);
assertThrows(IllegalStateException.class,
() -> helper.setPreference(new PreferenceCategory(mContext)));
}
@Test
public void onStart_noSuggestions_hidesGroup() {
assertThat(mGroup.isVisible()).isFalse();
}
@Test
public void onStart_startsSuggestionController() {
verify(mSuggestionController).start();
}
@Test
public void onStop_stopsSuggestionController() {
mControllerHelper.markState(Lifecycle.State.CREATED);
verify(mSuggestionController).stop();
}
@Test
public void onStop_destroysLoader() {
mControllerHelper.markState(Lifecycle.State.CREATED);
verify(mLoaderManager).destroyLoader(SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS);
}
@Test
public void onServiceConnected_restartsLoader() {
mController.onServiceConnected();
verify(mLoaderManager).restartLoader(
SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS, /* args= */ null, mController);
}
@Test
public void onServiceDisconnected_destroysLoader() {
mController.onServiceDisconnected();
verify(mLoaderManager).destroyLoader(SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS);
}
@Test
public void onCreateLoader_returnsSettingsSuggestionsLoader() {
assertThat(mController.onCreateLoader(
SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS, /* args= */ null)).isInstanceOf(
SettingsSuggestionsLoader.class);
}
@Test
public void onCreateLoader_unsupportedId_throwsIllegalArgumentException() {
assertThrows(IllegalArgumentException.class, () -> mController.onCreateLoader(
SettingsSuggestionsLoader.LOADER_ID_SUGGESTIONS + 1000, /* args= */ null));
}
@Test
public void onLoadFinished_groupContainsSuggestionPreference() {
mController.onLoadFinished(mLoader, Collections.singletonList(SUGGESTION_1));
assertThat(mGroup.getPreferenceCount()).isEqualTo(1);
Preference addedPref = mGroup.getPreference(0);
assertThat(addedPref).isInstanceOf(SuggestionPreference.class);
}
@Test
public void onLoadFinished_newSuggestion_addsToGroup() {
mController.onLoadFinished(mLoader, Collections.singletonList(SUGGESTION_1));
assertThat(mGroup.getPreferenceCount()).isEqualTo(1);
mController.onLoadFinished(mLoader, Arrays.asList(SUGGESTION_1, SUGGESTION_2));
assertThat(mGroup.getPreferenceCount()).isEqualTo(2);
}
@Test
public void onLoadFinished_removedSuggestion_removesFromGroup() {
mController.onLoadFinished(mLoader, Arrays.asList(SUGGESTION_1, SUGGESTION_2));
assertThat(mGroup.getPreferenceCount()).isEqualTo(2);
mController.onLoadFinished(mLoader, Collections.singletonList(SUGGESTION_2));
assertThat(mGroup.getPreferenceCount()).isEqualTo(1);
assertThat(((SuggestionPreference) mGroup.getPreference(0)).getSuggestion()).isEqualTo(
SUGGESTION_2);
}
@Test
public void onLoadFinished_noSuggestions_hidesGroup() {
mController.onLoadFinished(mLoader, Collections.singletonList(SUGGESTION_1));
assertThat(mGroup.isVisible()).isTrue();
mController.onLoadFinished(mLoader, Collections.emptyList());
assertThat(mGroup.isVisible()).isFalse();
}
@Test
public void launchSuggestion_sendsPendingIntent() throws PendingIntent.CanceledException {
PendingIntent pendingIntent = mock(PendingIntent.class);
Suggestion suggestion = new Suggestion.Builder("1").setPendingIntent(pendingIntent).build();
SuggestionPreference preference = new SuggestionPreference(mContext,
suggestion, /* callback= */ null);
mController.launchSuggestion(preference);
verify(pendingIntent).send();
}
@Test
public void launchSuggestion_callsSuggestionControllerLaunch() {
PendingIntent pendingIntent = mock(PendingIntent.class);
Suggestion suggestion = new Suggestion.Builder("1").setPendingIntent(pendingIntent).build();
SuggestionPreference preference = new SuggestionPreference(mContext,
suggestion, /* callback= */ null);
mController.launchSuggestion(preference);
verify(mSuggestionController).launchSuggestion(suggestion);
}
@Test
public void dismissSuggestion_removesSuggestion() {
mController.onLoadFinished(mLoader, Arrays.asList(SUGGESTION_1, SUGGESTION_2));
assertThat(mGroup.getPreferenceCount()).isEqualTo(2);
SuggestionPreference pref = (SuggestionPreference) mGroup.getPreference(0);
mController.dismissSuggestion(pref);
assertThat(mGroup.getPreferenceCount()).isEqualTo(1);
assertThat(((SuggestionPreference) mGroup.getPreference(0)).getSuggestion()).isEqualTo(
SUGGESTION_2);
}
@Test
public void dismissSuggestion_lastSuggestion_hidesGroup() {
mController.onLoadFinished(mLoader, Collections.singletonList(SUGGESTION_1));
SuggestionPreference pref = (SuggestionPreference) mGroup.getPreference(0);
mController.dismissSuggestion(pref);
assertThat(mGroup.isVisible()).isFalse();
}
@Test
public void dismissSuggestion_callsSuggestionControllerDismiss() {
mController.onLoadFinished(mLoader, Collections.singletonList(SUGGESTION_1));
SuggestionPreference pref = (SuggestionPreference) mGroup.getPreference(0);
mController.dismissSuggestion(pref);
verify(mSuggestionController).dismissSuggestions(pref.getSuggestion());
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import static com.android.car.ui.core.CarUi.requireToolbar;
import android.car.drivingstate.CarUxRestrictions;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.car.settings.R;
import com.android.car.settings.common.FragmentHost;
import com.android.car.settings.common.UxRestrictionsProvider;
import com.android.car.ui.toolbar.ToolbarController;
/**
* Test activity used for testing {@code BaseFragment} instances.
*/
public class BaseTestActivity extends FragmentActivity implements FragmentHost,
UxRestrictionsProvider {
private boolean mOnBackPressedFlag;
private CarUxRestrictions mRestrictionInfo = new CarUxRestrictions.Builder(/* reqOpt= */ true,
CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.car_setting_activity);
}
/**
* Places fragment in place of fragment container.
*
* @param fragment Fragment to add to activity.
*/
@Override
public void launchFragment(Fragment fragment) {
if (fragment instanceof DialogFragment) {
throw new IllegalArgumentException(
"cannot launch dialogs with launchFragment() - use showDialog() instead");
}
String tag = Integer.toString(getSupportFragmentManager().getBackStackEntryCount());
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment, tag)
.addToBackStack(null)
.commit();
}
@Override
public void showBlockingMessage() {
// no-op
}
@Override
public ToolbarController getToolbar() {
return requireToolbar(this);
}
@Override
public CarUxRestrictions getCarUxRestrictions() {
return mRestrictionInfo;
}
public void setCarUxRestrictions(CarUxRestrictions restrictionInfo) {
mRestrictionInfo = restrictionInfo;
}
/**
* Override to catch onBackPressed invocations on the activity.
*/
@Override
public void onBackPressed() {
mOnBackPressedFlag = true;
getSupportFragmentManager().popBackStackImmediate();
}
/**
* Gets a boolean flag indicating whether onBackPressed has been called.
*
* @return {@code true} if onBackPressed called, {@code false} otherwise.
*/
public boolean getOnBackPressedFlag() {
return mOnBackPressedFlag;
}
/**
* Clear the boolean flag for onBackPressed by setting it to false.
*/
public void clearOnBackPressedFlag() {
mOnBackPressedFlag = false;
}
@Override
public void goBack() {
onBackPressed();
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.widget.Button;
import android.widget.TextView;
import androidx.fragment.app.DialogFragment;
/**
* Helper methods for DialogFragment testing.
*/
public class DialogTestUtils {
private DialogTestUtils() {
}
/**
* Invokes onClick on the dialog's positive button.
*/
public static void clickPositiveButton(DialogFragment dialogFragment) {
Button positiveButton = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.button1);
positiveButton.callOnClick();
}
/**
* Invokes onClick on the dialog's negative button.
*/
public static void clickNegativeButton(DialogFragment dialogFragment) {
Button negativeButton = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.button2);
negativeButton.callOnClick();
}
/**
* Gets dialog's title.
*/
public static String getTitle(DialogFragment dialogFragment) {
TextView titleView = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.alertTitle);
return titleView.getText().toString();
}
public static String getMessage(DialogFragment dialogFragment) {
TextView messageView = dialogFragment.getDialog().getWindow().findViewById(
com.android.internal.R.id.message);
return messageView.getText().toString();
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import com.android.car.settings.R;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.android.controller.ComponentController;
/**
* Version of FragmentController that can be used for {@link androidx.fragment.app.Fragment} until
* upstream support is ready.
*/
public class FragmentController<F extends Fragment> extends
ComponentController<FragmentController<F>, F> {
private final F mFragment;
private final ActivityController<BaseTestActivity> mActivityController;
private FragmentController(F fragment) {
super(fragment);
mFragment = fragment;
mActivityController = ActivityController.of(new BaseTestActivity());
}
public static <F extends Fragment> FragmentController<F> of(F fragment) {
return new FragmentController<>(fragment);
}
/**
* Returns the fragment after attaching it to an activity, calling its onCreate() through
* onResume() lifecycle methods and making it visible.
*/
public F setup() {
return create().start().resume().visible().get();
}
/**
* Creates the activity with {@link Bundle} and adds the fragment to it.
*/
public FragmentController<F> create(final Bundle bundle) {
shadowMainLooper.runPaused(
() -> mActivityController
.create(bundle)
.get()
.getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, mFragment)
.commitNow());
return this;
}
@Override
public FragmentController<F> create() {
return create(null);
}
@Override
public FragmentController<F> destroy() {
shadowMainLooper.runPaused(mActivityController::destroy);
return this;
}
public FragmentController<F> start() {
shadowMainLooper.runPaused(mActivityController::start);
return this;
}
public FragmentController<F> resume() {
shadowMainLooper.runPaused(mActivityController::resume);
return this;
}
public FragmentController<F> pause() {
shadowMainLooper.runPaused(mActivityController::pause);
return this;
}
public FragmentController<F> stop() {
shadowMainLooper.runPaused(mActivityController::stop);
return this;
}
public FragmentController<F> visible() {
shadowMainLooper.runPaused(mActivityController::visible);
return this;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
import android.os.UserHandle;
import android.util.ArrayMap;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Implements(AccountManager.class)
public class ShadowAccountManager extends org.robolectric.shadows.ShadowAccountManager {
private final Map<Integer, List<Account>> mAccountsAsUserMap = new ArrayMap<>();
private final Map<Integer, List<AuthenticatorDescription>> mAuthenticatorAsUserMap =
new ArrayMap<>();
@Implementation
protected Account[] getAccountsAsUser(int userId) {
if (mAccountsAsUserMap.containsKey(userId)) {
return mAccountsAsUserMap.get(userId).toArray(new Account[]{});
}
return getAccounts();
}
public void addAccountAsUser(int userId, Account account) {
mAccountsAsUserMap.putIfAbsent(userId, new ArrayList<>());
mAccountsAsUserMap.get(userId).add(account);
}
@Implementation
protected Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) {
return getAccountsByType(type);
}
@Implementation
protected AuthenticatorDescription[] getAuthenticatorTypesAsUser(int userId) {
if (mAuthenticatorAsUserMap.containsKey(userId)) {
return mAuthenticatorAsUserMap.get(userId).toArray(new AuthenticatorDescription[]{});
}
return getAuthenticatorTypes();
}
public void addAuthenticatorAsUser(int userId, AuthenticatorDescription authenticator) {
mAuthenticatorAsUserMap.putIfAbsent(userId, new ArrayList<>());
mAuthenticatorAsUserMap.get(userId).add(authenticator);
}
@Override
public void removeAllAccounts() {
super.removeAllAccounts();
mAccountsAsUserMap.clear();
mAuthenticatorAsUserMap.clear();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.app.ActivityManager;
import android.content.pm.IPackageDataObserver;
import android.os.UserHandle;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(value = ActivityManager.class)
public class ShadowActivityManager extends org.robolectric.shadows.ShadowActivityManager {
private static final int DEFAULT_CURRENT_USER_ID = UserHandle.USER_SYSTEM;
private static boolean sIsApplicationUserDataCleared;
private static int sCurrentUserId = DEFAULT_CURRENT_USER_ID;
private String mMostRecentlyStoppedPackage;
@Resetter
public static void reset() {
sIsApplicationUserDataCleared = false;
sCurrentUserId = DEFAULT_CURRENT_USER_ID;
}
@Implementation
protected void forceStopPackage(String packageName) {
mMostRecentlyStoppedPackage = packageName;
}
@Implementation
protected boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
return sIsApplicationUserDataCleared;
}
public String getMostRecentlyStoppedPackage() {
return mMostRecentlyStoppedPackage;
}
public static void setApplicationUserDataCleared(boolean applicationUserDataCleared) {
sIsApplicationUserDataCleared = applicationUserDataCleared;
}
@Implementation
protected static int getCurrentUser() {
return sCurrentUserId;
}
public static void setCurrentUser(int userId) {
sCurrentUserId = userId;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.util.Pair;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Implements(value = AppOpsManager.class)
public class ShadowAppOpsManager {
private Table<Integer, InternalKey, Integer> mOpToKeyToMode = HashBasedTable.create();
@Implementation
protected void setMode(int code, int uid, String packageName, int mode) {
InternalKey key = new InternalKey(uid, packageName);
mOpToKeyToMode.put(code, key, mode);
}
/** Convenience method to get the mode directly instead of wrapped in an op list. */
public int getMode(int code, int uid, String packageName) {
Integer mode = mOpToKeyToMode.get(code, new InternalKey(uid, packageName));
return mode == null ? AppOpsManager.opToDefaultMode(code) : mode;
}
@Implementation
protected List<PackageOps> getPackagesForOps(int[] ops) {
if (ops == null) {
return Collections.emptyList();
}
ImmutableList.Builder<PackageOps> result = new ImmutableList.Builder<>();
for (int i = 0; i < ops.length; i++) {
int op = ops[i];
Map<InternalKey, Integer> keyToModeMap = mOpToKeyToMode.rowMap().get(op);
if (keyToModeMap == null) {
continue;
}
for (InternalKey key : keyToModeMap.keySet()) {
Integer mode = keyToModeMap.get(key);
if (mode == null) {
mode = AppOpsManager.opToDefaultMode(op);
}
OpEntry opEntry = new OpEntry(op, mode, Collections.emptyMap());
PackageOps packageOp = new PackageOps(key.mPackageName, key.mUid,
Collections.singletonList(opEntry));
result.add(packageOp);
}
}
return result.build();
}
private static class InternalKey {
private int mUid;
private String mPackageName;
InternalKey(int uid, String packageName) {
mUid = uid;
mPackageName = packageName;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof InternalKey) {
InternalKey that = (InternalKey) obj;
return mUid == that.mUid && mPackageName.equals(that.mPackageName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(mUid, mPackageName);
}
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ApplicationPackageManager;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Pair;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Shadow of ApplicationPackageManager that allows the getting of content providers per user. */
@Implements(value = ApplicationPackageManager.class)
public class ShadowApplicationPackageManager extends
org.robolectric.shadows.ShadowApplicationPackageManager {
private static Resources sResources = null;
private static PackageManager sPackageManager;
private final Map<Integer, String> mUserIdToDefaultBrowserMap = new HashMap<>();
private final Map<String, ComponentName> mPkgToDefaultActivityMap = new HashMap<>();
private final Map<String, IntentFilter> mPkgToDefaultActivityIntentFilterMap = new HashMap<>();
private final Map<IntentFilter, ComponentName> mPreferredActivities = new LinkedHashMap<>();
private final Map<Pair<String, Integer>, Integer> mPkgAndUserIdToIntentVerificationStatusMap =
new HashMap<>();
private List<ResolveInfo> mHomeActivities = Collections.emptyList();
private ComponentName mDefaultHomeActivity;
private String mPermissionControllerPackageName;
@Resetter
public static void reset() {
sResources = null;
sPackageManager = null;
}
@Implementation
@NonNull
protected List<ModuleInfo> getInstalledModules(@PackageManager.ModuleInfoFlags int flags) {
return Collections.emptyList();
}
@Implementation
protected Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
return icon;
}
@Override
@Implementation
protected ProviderInfo resolveContentProviderAsUser(String name, int flags,
@UserIdInt int userId) {
return resolveContentProvider(name, flags);
}
@Implementation
protected int getPackageUidAsUser(String packageName, int flags, int userId)
throws PackageManager.NameNotFoundException {
return 0;
}
@Implementation
protected void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) {
sPackageManager.deleteApplicationCacheFiles(packageName, observer);
}
@Implementation
protected Resources getResourcesForApplication(String appPackageName)
throws PackageManager.NameNotFoundException {
return sResources;
}
@Implementation
protected List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
return getInstalledApplications(flags);
}
@Implementation
protected ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
throws PackageManager.NameNotFoundException {
return getApplicationInfo(packageName, flags);
}
@Implementation
@Override
protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
outActivities.addAll(mHomeActivities);
return mDefaultHomeActivity;
}
@Implementation
@Override
protected void clearPackagePreferredActivities(String packageName) {
mPreferredActivities.clear();
}
@Implementation
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
for (IntentFilter filter : mPreferredActivities.keySet()) {
ComponentName name = mPreferredActivities.get(filter);
// If packageName is null, match everything, else filter by packageName.
if (packageName == null) {
outFilters.add(filter);
outActivities.add(name);
} else if (name.getPackageName().equals(packageName)) {
outFilters.add(filter);
outActivities.add(name);
}
}
return 0;
}
@Implementation
@Override
public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set,
ComponentName activity) {
mPreferredActivities.put(filter, activity);
}
@Implementation
@Override
protected String getDefaultBrowserPackageNameAsUser(int userId) {
return mUserIdToDefaultBrowserMap.getOrDefault(userId, null);
}
@Implementation
@Override
protected boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
mUserIdToDefaultBrowserMap.put(userId, packageName);
return true;
}
@Implementation
@Override
protected int getIntentVerificationStatusAsUser(String packageName, int userId) {
Pair<String, Integer> key = new Pair<>(packageName, userId);
return mPkgAndUserIdToIntentVerificationStatusMap.getOrDefault(key,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
}
@Implementation
@Override
protected boolean updateIntentVerificationStatusAsUser(String packageName, int status,
int userId) {
Pair<String, Integer> key = new Pair<>(packageName, userId);
mPkgAndUserIdToIntentVerificationStatusMap.put(key, status);
return true;
}
@Implementation
protected String getPermissionControllerPackageName() {
return mPermissionControllerPackageName;
}
public void setPermissionControllerPackageName(String packageName) {
mPermissionControllerPackageName = packageName;
}
public void setHomeActivities(List<ResolveInfo> homeActivities) {
mHomeActivities = homeActivities;
}
public void setDefaultHomeActivity(ComponentName defaultHomeActivity) {
mDefaultHomeActivity = defaultHomeActivity;
}
public static void setResources(Resources resources) {
sResources = resources;
}
public static void setPackageManager(PackageManager packageManager) {
sPackageManager = packageManager;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.app.Application;
import com.android.settingslib.applications.ApplicationsState;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(ApplicationsState.class)
public class ShadowApplicationsState {
private static ApplicationsState sApplicationsState;
public static void setInstance(ApplicationsState applicationsState) {
sApplicationsState = applicationsState;
}
@Resetter
public static void reset() {
sApplicationsState = null;
}
@Implementation
protected static ApplicationsState getInstance(Application app) {
return sApplicationsState;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.service.autofill.AutofillServiceInfo;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(AutofillServiceInfo.class)
public class ShadowAutofillServiceInfo {
private static String sSettingsActivity;
public void __constructor__(Context context, ServiceInfo si) {
// Do nothing when constructed in code.
}
@Resetter
public static void reset() {
sSettingsActivity = null;
}
@Implementation
protected String getSettingsActivity() {
return sSettingsActivity;
}
public static void setSettingsActivity(String settingsActivity) {
sSettingsActivity = settingsActivity;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.os.ParcelUuid;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowApplication;
import java.util.Collections;
import java.util.List;
@Implements(BluetoothAdapter.class)
public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter {
private static int sResetCalledCount = 0;
private String mName;
private int mScanMode;
public static boolean verifyFactoryResetCalled(int numTimes) {
return sResetCalledCount == numTimes;
}
@Implementation
protected boolean factoryReset() {
sResetCalledCount++;
return true;
}
@Implementation
protected static synchronized BluetoothAdapter getDefaultAdapter() {
return (BluetoothAdapter) ShadowApplication.getInstance().getBluetoothAdapter();
}
@Implementation
protected ParcelUuid[] getUuids() {
return null;
}
@Implementation
protected String getName() {
return mName;
}
@Implementation
protected boolean setName(String name) {
if (getState() != STATE_ON) {
return false;
}
mName = name;
return true;
}
@Implementation
protected int getScanMode() {
if (getState() != STATE_ON) {
return SCAN_MODE_NONE;
}
return mScanMode;
}
@Implementation
protected Object setScanMode(int scanMode) {
if (getState() != STATE_ON) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
mScanMode = scanMode;
return BluetoothStatusCodes.SUCCESS;
}
@Implementation
protected List<Integer> getSupportedProfiles() {
return Collections.emptyList();
}
@Resetter
public static void reset() {
sResetCalledCount = 0;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2018 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.car.settings.testutils;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(BluetoothPan.class)
public class ShadowBluetoothPan {
@Implementation
public void __constructor__(Context context, BluetoothProfile.ServiceListener l) {
// Do nothing. Implemented to avoid NullPointerException in BluetoothPan.
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.car.Car;
import android.car.CarNotConnectedException;
import android.content.Context;
import android.content.ServiceConnection;
import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
/**
* Shadow class for {@link Car}. Components in car support library expects
* this class to be available at run time.
*/
@Implements(Car.class)
public class ShadowCar {
private static Car sMockCar = mock(Car.class);
private static boolean sIsConnected;
private static String sServiceName;
private static Object sCarManager;
/**
* Returns a mocked version of a {@link Car} object.
*/
@Implementation
protected static Car createCar(Context context, ServiceConnection serviceConnection) {
if (serviceConnection != null) {
doAnswer((Answer<Void>) invocation -> {
serviceConnection.onServiceConnected(null, null);
return null;
}).when(sMockCar).connect();
doAnswer((Answer<Void>) invocation -> {
serviceConnection.onServiceDisconnected(null);
return null;
}).when(sMockCar).disconnect();
}
doReturn(sIsConnected).when(sMockCar).isConnected();
if (sServiceName != null) {
try {
doReturn(sCarManager).when(sMockCar).getCarManager(sServiceName);
} catch (CarNotConnectedException e) {
// do nothing, have to do this because compiler doesn't understand mock can't throw
// exception.
}
}
return sMockCar;
}
/**
* Returns a mocked version of a {@link Car} object.
*/
@Implementation
protected static Car createCar(Context context) {
doReturn(sIsConnected).when(sMockCar).isConnected();
if (sServiceName != null) {
try {
doReturn(sCarManager).when(sMockCar).getCarManager(sServiceName);
} catch (CarNotConnectedException e) {
// do nothing, have to do this because compiler doesn't understand mock can't throw
// exception.
}
}
return sMockCar;
}
/**
* Sets the manager returned by {@link Car#getCarManager(String)}.
*
* @param serviceName the name for the service request that should return this car manager.
* @param carManager the object returned by a call with this service.
*/
public static void setCarManager(String serviceName, Object carManager) {
sServiceName = serviceName;
sCarManager = carManager;
try {
doReturn(carManager).when(sMockCar).getCarManager(serviceName);
} catch (CarNotConnectedException e) {
// do nothing, have to do this because compiler doesn't understand mock can't throw e.
}
}
/**
* Resets the shadow state, note this will not remove stubbed behavior on references to older
* calls to {@link #createCar(Context, ServiceConnection)}.
*/
@Resetter
public static void reset() {
sMockCar = mock(Car.class);
sServiceName = null;
sCarManager = null;
sIsConnected = false;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import com.android.car.settings.units.CarUnitsManager;
import com.android.car.settings.units.Unit;
import com.android.car.settings.units.UnitsMap;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
/**
* Shadow class for {@link CarUnitsManager}.
*/
@Implements(CarUnitsManager.class)
public class ShadowCarUnitsManager {
private static boolean sConnected = false;
private static CarUnitsManager.OnCarServiceListener sListener;
private static HashMap<Integer, Unit[]> sSupportedUnits = new HashMap<>();
private static HashMap<Integer, Unit> sUnitsBeingUsed = new HashMap<>();
@Implementation
protected void connect() {
sConnected = true;
}
@Implementation
protected void disconnect() {
sConnected = false;
}
@Implementation
protected static Unit[] getUnitsSupportedByProperty(int propertyId) {
return sSupportedUnits.get(propertyId);
}
@Implementation
protected static Unit getUnitUsedByProperty(int propertyId) {
return sUnitsBeingUsed.get(propertyId);
}
@Implementation
protected static void setUnitUsedByProperty(int propertyId, int unitId) {
sUnitsBeingUsed.put(propertyId, UnitsMap.MAP.get(unitId));
}
@Implementation
protected static void registerCarServiceListener(
CarUnitsManager.OnCarServiceListener listener) {
sListener = listener;
}
@Implementation
protected static void unregisterCarServiceListener() {
sListener = null;
}
@Resetter
public static void reset() {
sConnected = false;
sListener = null;
sSupportedUnits = new HashMap<>();
sUnitsBeingUsed = new HashMap<>();
}
public static void setUnitsSupportedByProperty(int propertyId, Unit[] units) {
sSupportedUnits.put(propertyId, units);
}
public static boolean isConnected() {
return sConnected;
}
public static CarUnitsManager.OnCarServiceListener getListener() {
return sListener;
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Context;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
import com.android.car.settings.wifi.CarWifiManager;
import com.android.wifitrackerlib.WifiEntry;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
/** TODO(b/148971715): Refactor all methods to run without relying on sInstance. */
@Implements(CarWifiManager.class)
public class ShadowCarWifiManager {
public static final int STATE_UNKNOWN = -1;
public static final int STATE_STARTED = 0;
public static final int STATE_STOPPED = 1;
public static final int STATE_DESTROYED = 2;
private static CarWifiManager sInstance;
private static int sCurrentState = STATE_UNKNOWN;
private static SoftApConfiguration sSoftApConfiguration =
new SoftApConfiguration.Builder().build();
private static boolean sIs5GhzBandSupported = true;
private static int sWifiState = WifiManager.WIFI_STATE_UNKNOWN;
public static void setInstance(CarWifiManager wifiManager) {
sInstance = wifiManager;
}
@Resetter
public static void reset() {
sInstance = null;
sSoftApConfiguration = new SoftApConfiguration.Builder().build();
sCurrentState = STATE_UNKNOWN;
sIs5GhzBandSupported = true;
sWifiState = WifiManager.WIFI_STATE_UNKNOWN;
}
@Implementation
public void __constructor__(Context context) {
}
@Implementation
public void start() {
sCurrentState = STATE_STARTED;
}
@Implementation
public void stop() {
sCurrentState = STATE_STOPPED;
}
@Implementation
public void destroy() {
sCurrentState = STATE_DESTROYED;
}
@Implementation
public void setSoftApConfig(SoftApConfiguration config) {
sSoftApConfiguration = config;
}
@Implementation
public SoftApConfiguration getSoftApConfig() {
return sSoftApConfiguration;
}
@Implementation
public boolean isWifiEnabled() {
return sInstance.isWifiEnabled();
}
@Implementation
public int getWifiState() {
return sWifiState;
}
public static void setWifiState(int wifiState) {
sWifiState = wifiState;
}
@Implementation
public boolean isWifiApEnabled() {
return sInstance.isWifiApEnabled();
}
@Implementation
public List<WifiEntry> getAllWifiEntries() {
return sInstance.getAllWifiEntries();
}
@Implementation
public List<WifiEntry> getSavedWifiEntries() {
return sInstance.getSavedWifiEntries();
}
@Implementation
protected String getCountryCode() {
return "1";
}
@Implementation
protected boolean is5GhzBandSupported() {
return sIs5GhzBandSupported;
}
@Implementation
protected boolean addListener(CarWifiManager.Listener listener) {
return sInstance.addListener(listener);
}
public static void setIs5GhzBandSupported(boolean supported) {
sIs5GhzBandSupported = supported;
}
public static int getCurrentState() {
return sCurrentState;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.util.SparseArray;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(CarrierConfigManager.class)
public class ShadowCarrierConfigManager {
private SparseArray<PersistableBundle> mBundles = new SparseArray<>();
@Implementation
protected PersistableBundle getConfigForSubId(int subId) {
return mBundles.get(subId);
}
public void setConfigForSubId(int subId, PersistableBundle config) {
mBundles.put(subId, config);
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import static org.mockito.Mockito.mock;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Handler;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(ConnectivityManager.class)
public class ShadowConnectivityManager extends org.robolectric.shadows.ShadowConnectivityManager {
private static int sResetCalledCount = 0;
private final Map<Network, NetworkCapabilities> mCapabilitiesMap = new HashMap<>();
private int mStartTetheringCalledCount = 0;
private int mStopTetheringCalledCount = 0;
private int mTetheringType;
public static boolean verifyFactoryResetCalled(int numTimes) {
return sResetCalledCount == numTimes;
}
public boolean verifyStartTetheringCalled(int numTimes) {
return mStartTetheringCalledCount == numTimes;
}
public boolean verifyStopTetheringCalled(int numTimes) {
return mStopTetheringCalledCount == numTimes;
}
public int getTetheringType() {
return mTetheringType;
}
public void addNetworkCapabilities(Network network, NetworkCapabilities capabilities) {
super.addNetwork(network, mock(NetworkInfo.class));
mCapabilitiesMap.put(network, capabilities);
}
@Implementation
protected NetworkCapabilities getNetworkCapabilities(Network network) {
return mCapabilitiesMap.get(network);
}
@Implementation
public void startTethering(int type, boolean showProvisioningUi,
final ConnectivityManager.OnStartTetheringCallback callback, Handler handler) {
mTetheringType = type;
mStartTetheringCalledCount++;
}
@Implementation
public void stopTethering(int type) {
mTetheringType = type;
mStopTetheringCalledCount++;
}
@Implementation
protected void factoryReset() {
sResetCalledCount++;
}
@Resetter
public static void reset() {
sResetCalledCount = 0;
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.accounts.Account;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncStatusInfo;
import android.content.SyncStatusObserver;
import android.os.Bundle;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Derived from {@link com.android.settings.testutils.shadow.ShadowContentResolver}
*
* <p>Needed for many account-related tests because the default ShadowContentResolver does not
* include an implementation of getSyncAdapterTypesAsUser, which is used by {@link
* com.android.settingslib.accounts.AuthenticatorHelper#buildAccountTypeToAuthoritiesMap}.
*/
@Implements(ContentResolver.class)
public class ShadowContentResolver extends org.robolectric.shadows.ShadowContentResolver {
private static final int SYNCABLE = 1;
private static SyncAdapterType[] sSyncAdapterTypes = new SyncAdapterType[0];
private static Map<String, Integer> sSyncable = new HashMap<>();
private static Map<String, Boolean> sSyncAutomatically = new HashMap<>();
private static Map<Integer, Boolean> sMasterSyncAutomatically = new HashMap<>();
private static Map<String, SyncStatusInfo> sSyncStatus = new HashMap<>();
private static List<SyncInfo> sSyncs = new ArrayList<>();
private static SyncListener sSyncListener;
private static SyncStatusObserver sStatusObserver;
@Implementation
protected static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
return sSyncAdapterTypes;
}
@Implementation
protected static int getIsSyncableAsUser(Account account, String authority, int userId) {
return sSyncable.getOrDefault(authority, SYNCABLE);
}
@Implementation
protected static boolean getSyncAutomaticallyAsUser(Account account, String authority,
int userId) {
return sSyncAutomatically.getOrDefault(authority, true);
}
@Implementation
protected static boolean getMasterSyncAutomaticallyAsUser(int userId) {
return sMasterSyncAutomatically.getOrDefault(userId, true);
}
@Implementation
protected static List<SyncInfo> getCurrentSyncsAsUser(@UserIdInt int userId) {
return sSyncs;
}
@Implementation
protected static SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
@UserIdInt int userId) {
return sSyncStatus.get(authority);
}
public static void setSyncAdapterTypes(SyncAdapterType[] syncAdapterTypes) {
sSyncAdapterTypes = syncAdapterTypes;
}
@Implementation
public static void setIsSyncable(Account account, String authority, int syncable) {
sSyncable.put(authority, syncable);
}
@Implementation
protected static void setSyncAutomaticallyAsUser(Account account, String authority,
boolean sync, @UserIdInt int userId) {
sSyncAutomatically.put(authority, sync);
}
@Implementation
protected static void setMasterSyncAutomaticallyAsUser(boolean sync, @UserIdInt int userId) {
sMasterSyncAutomatically.put(userId, sync);
}
public static void setCurrentSyncs(List<SyncInfo> syncs) {
sSyncs = syncs;
}
public static void setSyncStatus(Account account, String authority, SyncStatusInfo status) {
sSyncStatus.put(authority, status);
}
@Implementation
public static void cancelSyncAsUser(Account account, String authority, @UserIdInt int userId) {
if (sSyncListener != null) {
sSyncListener.onSyncCanceled(account, authority, userId);
}
}
@Implementation
public static void requestSyncAsUser(Account account, String authority, @UserIdInt int userId,
Bundle extras) {
if (sSyncListener != null) {
sSyncListener.onSyncRequested(account, authority, userId, extras);
}
}
public static void setSyncListener(SyncListener syncListener) {
sSyncListener = syncListener;
}
@Implementation
protected static Object addStatusChangeListener(int mask, SyncStatusObserver callback) {
sStatusObserver = callback;
return null;
}
@Implementation
protected static void removeStatusChangeListener(Object handle) {
sStatusObserver = null;
}
public static SyncStatusObserver getStatusChangeListener() {
return sStatusObserver;
}
@Resetter
public static void reset() {
org.robolectric.shadows.ShadowContentResolver.reset();
sSyncable.clear();
sSyncAutomatically.clear();
sMasterSyncAutomatically.clear();
sSyncAdapterTypes = new SyncAdapterType[0];
sSyncStatus.clear();
sSyncs = new ArrayList<>();
sSyncListener = null;
sStatusObserver = null;
}
/**
* A listener interface that can be used to verify calls to {@link #cancelSyncAsUser} and {@link
* #requestSyncAsUser}
*/
public interface SyncListener {
void onSyncCanceled(Account account, String authority, @UserIdInt int userId);
void onSyncRequested(Account account, String authority, @UserIdInt int userId,
Bundle extras);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.net.NetworkTemplate;
import com.android.settingslib.net.DataUsageController;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(DataUsageController.class)
public class ShadowDataUsageController {
private static DataUsageController sInstance;
public static void setInstance(DataUsageController dataUsageController) {
sInstance = dataUsageController;
}
@Implementation
protected DataUsageController.DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
return sInstance.getDataUsageInfo(template);
}
@Resetter
public static void reset() {
sInstance = null;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.Context;
import android.telecom.DefaultDialerManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(DefaultDialerManager.class)
public class ShadowDefaultDialerManager {
private static String sDefaultDialerPackage;
@Resetter
public static void reset() {
sDefaultDialerPackage = null;
}
@Implementation
protected static String getDefaultDialerApplication(Context context) {
return sDefaultDialerPackage;
}
public static void setDefaultDialerApplication(String defaultDialerPackage) {
sDefaultDialerPackage = defaultDialerPackage;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.util.ArraySet;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
import java.util.Set;
@Implements(value = DevicePolicyManager.class)
public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDevicePolicyManager {
@Nullable
private static List<String> sPermittedInputMethods;
private Set<String> mActiveAdminsPackages = new ArraySet<>();
private boolean mIsInstallInQueue;
@Resetter
public static void reset() {
sPermittedInputMethods = null;
}
@Implementation
@Nullable
protected List<String> getPermittedInputMethodsForCurrentUser() {
return sPermittedInputMethods;
}
public static void setPermittedInputMethodsForCurrentUser(@Nullable List<String> inputMethods) {
sPermittedInputMethods = inputMethods;
}
@Implementation
protected boolean packageHasActiveAdmins(String packageName) {
return mActiveAdminsPackages.contains(packageName);
}
public void setPackageHasActiveAdmins(String packageName, boolean hasActiveAdmins) {
if (hasActiveAdmins) {
mActiveAdminsPackages.add(packageName);
} else {
mActiveAdminsPackages.remove(packageName);
}
}
@Implementation
protected boolean isUninstallInQueue(String packageName) {
return mIsInstallInQueue;
}
public void setIsUninstallInQueue(boolean isUninstallInQueue) {
mIsInstallInQueue = isUninstallInQueue;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.hardware.usb.IUsbManager;
import android.os.IBinder;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(value = IUsbManager.Stub.class)
public class ShadowIUsbManager {
private static IUsbManager sInstance;
public static void setInstance(IUsbManager instance) {
sInstance = instance;
}
@Implementation
public static IUsbManager asInterface(IBinder obj) {
return sInstance;
}
@Resetter
public static void reset() {
sInstance = null;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.IconDrawableFactory;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(value = IconDrawableFactory.class)
public class ShadowIconDrawableFactory {
@Implementation
protected Drawable getBadgedIcon(ApplicationInfo appInfo) {
return new ColorDrawable(0);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.provider.Settings;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import androidx.annotation.Nullable;
import com.android.car.settings.inputmethod.InputMethodUtil;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Implements(value = InputMethodManager.class)
public class ShadowInputMethodManager extends org.robolectric.shadows.ShadowInputMethodManager {
private static List<InputMethodSubtype> sInputMethodSubtypes = new ArrayList<>();
private static List<InputMethodInfo> sInputMethodList = new ArrayList<>();
private static Map<String, InputMethodInfo> sInputMethodMap = new HashMap<>();
@Resetter
public static void reset() {
sInputMethodSubtypes.clear();
sInputMethodList.clear();
sInputMethodMap.clear();
org.robolectric.shadows.ShadowInputMethodManager.reset();
}
public static void setEnabledInputMethodSubtypeList(List<InputMethodSubtype> list) {
sInputMethodSubtypes = list;
}
@Implementation
protected List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
boolean allowsImplicitlySelectedSubtypes) {
return sInputMethodSubtypes;
}
public static void setEnabledInputMethodList(@Nullable List<InputMethodInfo> list) {
if (list == null || list.size() == 0) {
Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, "");
return;
}
String concatenatedInputMethodIds = createInputMethodIdString(list.stream().map(
imi -> imi.getId()).collect(Collectors.toList()).toArray(new String[list.size()]));
Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, concatenatedInputMethodIds);
addInputMethodInfosToMap(list);
}
@Implementation
protected List<InputMethodInfo> getEnabledInputMethodList() {
List<InputMethodInfo> enabledInputMethodList = new ArrayList<>();
String inputMethodIdString = Settings.Secure.getString(
RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
if (inputMethodIdString == null || inputMethodIdString.isEmpty()) {
return enabledInputMethodList;
}
InputMethodUtil.sInputMethodSplitter.setString(inputMethodIdString);
while (InputMethodUtil.sInputMethodSplitter.hasNext()) {
enabledInputMethodList.add(sInputMethodMap.get(InputMethodUtil.sInputMethodSplitter
.next()));
}
return enabledInputMethodList;
}
public void setInputMethodList(List<InputMethodInfo> inputMethodInfos) {
sInputMethodList = inputMethodInfos;
if (inputMethodInfos == null) {
return;
}
addInputMethodInfosToMap(inputMethodInfos);
}
@Implementation
protected List<InputMethodInfo> getInputMethodList() {
return sInputMethodList;
}
private static String createInputMethodIdString(String... ids) {
int size = ids == null ? 0 : ids.length;
if (size == 1) {
return ids[0];
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
builder.append(ids[i]);
if (i != size - 1) {
builder.append(InputMethodUtil.INPUT_METHOD_DELIMITER);
}
}
return builder.toString();
}
private static void addInputMethodInfosToMap(List<InputMethodInfo> inputMethodInfos) {
if (sInputMethodMap == null || sInputMethodMap.size() == 0) {
sInputMethodMap = inputMethodInfos.stream().collect(Collectors.toMap(
InputMethodInfo::getId, imi -> imi));
return;
}
inputMethodInfos.forEach(imi -> {
sInputMethodMap.put(imi.getId(), imi);
});
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.bluetooth.BluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(LocalBluetoothAdapter.class)
public class ShadowLocalBluetoothAdapter {
private int mState = BluetoothAdapter.STATE_OFF;
private boolean mIsBluetoothEnabled = true;
private int mScanMode = BluetoothAdapter.SCAN_MODE_NONE;
@Implementation
protected boolean isEnabled() {
return mIsBluetoothEnabled;
}
@Implementation
protected boolean enable() {
mIsBluetoothEnabled = true;
return true;
}
@Implementation
protected boolean disable() {
mIsBluetoothEnabled = false;
return true;
}
@Implementation
protected int getScanMode() {
return mScanMode;
}
@Implementation
protected void setScanMode(int mode) {
mScanMode = mode;
}
@Implementation
protected boolean setScanMode(int mode, int duration) {
mScanMode = mode;
return true;
}
@Implementation
protected int getState() {
return mState;
}
public void setState(int state) {
mState = state;
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Implements(LocalBroadcastManager.class)
public class ShadowLocalBroadcastManager {
private static List<Intent> sSentBroadcastIntents = new ArrayList<>();
private static List<Wrapper> sRegisteredReceivers = new ArrayList<>();
@Implementation
public static LocalBroadcastManager getInstance(final Context context) {
return ShadowApplication.getInstance().getSingleton(LocalBroadcastManager.class,
() -> ReflectionHelpers.callConstructor(LocalBroadcastManager.class,
ClassParameter.from(Context.class, context)));
}
@Implementation
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
sRegisteredReceivers.add(new Wrapper(receiver, filter));
}
@Implementation
public void unregisterReceiver(BroadcastReceiver receiver) {
Iterator<Wrapper> iterator = sRegisteredReceivers.iterator();
while (iterator.hasNext()) {
Wrapper wrapper = iterator.next();
if (wrapper.getBroadcastReceiver() == receiver) {
iterator.remove();
}
}
}
@Implementation
public boolean sendBroadcast(Intent intent) {
boolean sent = false;
sSentBroadcastIntents.add(intent);
List<Wrapper> copy = new ArrayList<>(sRegisteredReceivers);
for (Wrapper wrapper : copy) {
if (wrapper.getIntentFilter().matchAction(intent.getAction())) {
int match = wrapper.getIntentFilter().matchData(intent.getType(),
intent.getScheme(), intent.getData());
if (match != IntentFilter.NO_MATCH_DATA && match != IntentFilter.NO_MATCH_TYPE) {
sent = true;
final BroadcastReceiver receiver = wrapper.getBroadcastReceiver();
final Intent broadcastIntent = intent;
Robolectric.getForegroundThreadScheduler().post(
(Runnable) () -> receiver.onReceive(RuntimeEnvironment.application,
broadcastIntent));
}
}
}
return sent;
}
@Resetter
public static void reset() {
sSentBroadcastIntents.clear();
sRegisteredReceivers.clear();
}
public static List<Intent> getSentBroadcastIntents() {
return sSentBroadcastIntents;
}
public static List<Wrapper> getRegisteredBroadcastReceivers() {
return sRegisteredReceivers;
}
public static class Wrapper {
private final BroadcastReceiver mBroadcastReceiver;
private final IntentFilter mIntentFilter;
public Wrapper(BroadcastReceiver broadcastReceiver, IntentFilter intentFilter) {
this.mBroadcastReceiver = broadcastReceiver;
this.mIntentFilter = intentFilter;
}
public BroadcastReceiver getBroadcastReceiver() {
return mBroadcastReceiver;
}
public IntentFilter getIntentFilter() {
return mIntentFilter;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Context;
import com.android.internal.app.LocaleStore;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@Implements(LocaleStore.class)
public class ShadowLocaleStore {
private static Map<LocaleStore.LocaleInfo, Set<LocaleStore.LocaleInfo>>
sLocaleInfoRelationships = new HashMap<>();
public static void addLocaleRelationship(Locale parent, Locale child) {
LocaleStore.LocaleInfo parentInfo = LocaleStore.getLocaleInfo(parent);
LocaleStore.LocaleInfo childInfo = LocaleStore.getLocaleInfo(child);
Set<LocaleStore.LocaleInfo> set = sLocaleInfoRelationships.getOrDefault(parentInfo,
new HashSet<>());
set.add(childInfo);
sLocaleInfoRelationships.put(parentInfo, set);
}
@Resetter
public static void reset() {
sLocaleInfoRelationships.clear();
}
@Implementation
protected static Set<LocaleStore.LocaleInfo> getLevelLocales(Context context,
Set<String> ignorables, LocaleStore.LocaleInfo parent, boolean translatedOnly) {
if (parent != null) {
return sLocaleInfoRelationships.getOrDefault(parent, new HashSet<>());
} else {
return sLocaleInfoRelationships.keySet();
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Intent;
import android.location.LocationManager;
import android.os.UserHandle;
import android.provider.Settings;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(value = LocationManager.class)
public class ShadowLocationManager {
@Implementation
protected void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
int newMode = enabled
? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
: Settings.Secure.LOCATION_MODE_OFF;
Settings.Secure.putIntForUser(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.LOCATION_MODE, newMode, userHandle.getIdentifier());
RuntimeEnvironment.application.sendBroadcast(new Intent(
LocationManager.MODE_CHANGED_ACTION));
}
@Implementation
protected boolean isLocationEnabled() {
return Settings.Secure.getInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
!= Settings.Secure.LOCATION_MODE_OFF;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.app.admin.DevicePolicyManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
/**
* Shadow for LockPatternUtils.
*/
@Implements(LockPatternUtils.class)
public class ShadowLockPatternUtils {
public static final int NO_USER = -1;
private static int sPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
private static byte[] sSavedPassword;
private static byte[] sSavedPattern;
private static LockscreenCredential sClearLockCredential;
private static int sClearLockUser = NO_USER;
@Resetter
public static void reset() {
sPasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
sSavedPassword = null;
sSavedPattern = null;
sClearLockCredential = null;
sClearLockUser = NO_USER;
}
/**
* Sets the current password quality that is returned by
* {@link LockPatternUtils#getKeyguardStoredPasswordQuality}.
*/
public static void setPasswordQuality(int passwordQuality) {
sPasswordQuality = passwordQuality;
}
/**
* Returns the password saved by a call to {@link LockPatternUtils#saveLockPassword}.
*/
public static byte[] getSavedPassword() {
return sSavedPassword;
}
/**
* Returns the saved credential passed in to clear the lock, null if it has not been cleared.
*/
public static LockscreenCredential getClearLockCredential() {
return sClearLockCredential;
}
/**
* Returns the user passed in to clear the lock, {@link #NO_USER} if it has not been cleared.
*/
public static int getClearLockUser() {
return sClearLockUser;
}
/**
* Returns the pattern saved by a call to {@link LockPatternUtils#saveLockPattern}.
*/
public static byte[] getSavedPattern() {
return sSavedPattern;
}
@Implementation
protected int getKeyguardStoredPasswordQuality(int userHandle) {
return sPasswordQuality;
}
@Implementation
public boolean setLockCredential(LockscreenCredential newCredential,
LockscreenCredential savedCredential, int userId) {
if (newCredential.isPassword() || newCredential.isPin()) {
sSavedPassword = newCredential.duplicate().getCredential();
} else if (newCredential.isPattern()) {
sSavedPattern = newCredential.duplicate().getCredential();
} else if (newCredential.isNone()) {
sClearLockCredential = savedCredential.duplicate();
sClearLockUser = userId;
}
return true;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 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.car.settings.testutils;
import android.net.NetworkPolicy;
import android.net.NetworkTemplate;
import com.android.settingslib.NetworkPolicyEditor;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(NetworkPolicyEditor.class)
public class ShadowNetworkPolicyEditor {
private static NetworkPolicy sNetworkPolicy;
@Implementation
public NetworkPolicy getPolicy(NetworkTemplate template) {
return sNetworkPolicy;
}
public static void setNetworkPolicy(NetworkPolicy networkPolicy) {
sNetworkPolicy = networkPolicy;
}
@Resetter
public static void reset() {
sNetworkPolicy = null;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Context;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.util.Pair;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Implements(NetworkPolicyManager.class)
public class ShadowNetworkPolicyManager {
private static NetworkPolicyManager sNetworkPolicyManager;
private static Iterator<Pair<ZonedDateTime, ZonedDateTime>> sCycleIterator;
private static Map<String, Integer> sResetCalledForSubscriberCount = new HashMap<>();
public static boolean verifyFactoryResetCalled(String subscriber, int numTimes) {
if (!sResetCalledForSubscriberCount.containsKey(subscriber)) return false;
return sResetCalledForSubscriberCount.get(subscriber) == numTimes;
}
@Implementation
protected void factoryReset(String subscriber) {
sResetCalledForSubscriberCount.put(subscriber,
sResetCalledForSubscriberCount.getOrDefault(subscriber, 0) + 1);
}
@Implementation
protected int[] getUidsWithPolicy(int policy) {
return sNetworkPolicyManager == null ? new int[0] : sNetworkPolicyManager
.getUidsWithPolicy(policy);
}
@Implementation
protected static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(
NetworkPolicy policy) {
return sCycleIterator;
}
public static void setCycleIterator(
Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator) {
sCycleIterator = cycleIterator;
}
@Implementation
public static NetworkPolicyManager from(Context context) {
return sNetworkPolicyManager;
}
public static void setNetworkPolicyManager(NetworkPolicyManager networkPolicyManager) {
sNetworkPolicyManager = networkPolicyManager;
}
@Resetter
public static void reset() {
sResetCalledForSubscriberCount.clear();
sCycleIterator = null;
sNetworkPolicyManager = null;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.permission.PermissionControllerManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.List;
/**
* A RuntimePermissionPresenter that changes nothing and <u>never returns callbacks</u>.
*/
@Implements(PermissionControllerManager.class)
public class ShadowPermissionControllerManager {
@Implementation
protected void __constructor__(Context context, @NonNull Handler handler) {
// no nothing, everything is shadowed
}
@Implementation
protected void getAppPermissions(String packageName,
PermissionControllerManager.OnGetAppPermissionResultCallback callback,
Handler handler) {
}
@Implementation
protected void revokeRuntimePermission(String packageName, String permissionName) {
}
@Implementation
protected void countPermissionApps(List<String> permissionNames, boolean countOnlyGranted,
boolean countSystem,
PermissionControllerManager.OnCountPermissionAppsResultCallback callback,
Handler handler) {
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Context;
import android.os.RecoverySystem;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(RecoverySystem.class)
public class ShadowRecoverySystem {
private static int sWipeEuiccDataCalledCount = 0;
public static boolean verifyWipeEuiccDataCalled(int numTimes) {
return sWipeEuiccDataCalledCount == numTimes;
}
@Implementation
protected static boolean wipeEuiccData(Context context, final String packageName) {
sWipeEuiccDataCalledCount++;
return true;
}
@Resetter
public static void reset() {
sWipeEuiccDataCalledCount = 0;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.content.Context;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(RestrictedLockUtilsInternal.class)
public class ShadowRestrictedLockUtilsInternal {
private static RestrictedLockUtils.EnforcedAdmin sEnforcedAdmin;
private static boolean sHasBaseUserRestriction;
@Resetter
public static void reset() {
sEnforcedAdmin = null;
sHasBaseUserRestriction = false;
}
public static void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
sEnforcedAdmin = enforcedAdmin;
}
public static void setHasBaseUserRestriction(boolean hasBaseUserRestriction) {
sHasBaseUserRestriction = hasBaseUserRestriction;
}
public static void sendShowAdminSupportDetailsIntent(Context context,
RestrictedLockUtils.EnforcedAdmin admin) {
// do nothing
}
@Implementation
protected static RestrictedLockUtils.EnforcedAdmin checkIfRestrictionEnforced(Context context,
String userRestriction, int userId) {
return sEnforcedAdmin;
}
@Implementation
protected static boolean hasBaseUserRestriction(Context context,
String userRestriction, int userId) {
return sHasBaseUserRestriction;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Context;
import android.media.Ringtone;
import android.net.Uri;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(Ringtone.class)
public class ShadowRingtone {
private static Map<Uri, String> sTitleForGivenUri = new HashMap<>();
public static void setExpectedTitleForUri(Uri uri, String title) {
sTitleForGivenUri.put(uri, title);
}
@Implementation
protected static String getTitle(
Context context, Uri uri, boolean followSettingsUri, boolean allowRemote) {
return sTitleForGivenUri.getOrDefault(uri, null);
}
@Resetter
public static void reset() {
sTitleForGivenUri.clear();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.Context;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.HashMap;
import java.util.Map;
@Implements(RingtoneManager.class)
public class ShadowRingtoneManager {
private static Ringtone sRingtone;
private static Map<Integer, Uri> sSetDefaultRingtoneCalled = new HashMap<>();
public static void setRingtone(Ringtone ringtone) {
sRingtone = ringtone;
}
@Implementation
protected static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
return sRingtone;
}
@Implementation
public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
sSetDefaultRingtoneCalled.put(type, ringtoneUri);
}
@Implementation
public static Uri getActualDefaultRingtoneUri(Context context, int type) {
return sSetDefaultRingtoneCalled.getOrDefault(type, null);
}
@Resetter
public static void reset() {
sRingtone = null;
sSetDefaultRingtoneCalled.clear();
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import android.content.ContentResolver;
import android.provider.Settings;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowSettings;
import org.robolectric.util.ReflectionHelpers;
@Implements(Settings.Secure.class)
public class ShadowSecureSettings extends ShadowSettings.ShadowSecure {
@Implementation
protected static int getInt(ContentResolver resolver, String name) {
return Shadow.directlyOn(
Settings.Secure.class,
"getInt",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name));
}
@Implementation
protected static int getInt(ContentResolver resolver, String name, int def) {
return Shadow.directlyOn(
Settings.Secure.class,
"getInt",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, def));
}
@Implementation
protected static int getIntForUser(ContentResolver resolver, String name, int def,
int userHandle) {
return Shadow.directlyOn(
Settings.Secure.class,
"getIntForUser",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, def),
ReflectionHelpers.ClassParameter.from(int.class, userHandle));
}
@Implementation
protected static boolean putInt(ContentResolver resolver, String name, int value) {
return Shadow.directlyOn(
Settings.Secure.class,
"putInt",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, value));
}
@Implementation
protected static boolean putIntForUser(ContentResolver resolver, String name, int value,
int userHandle) {
return Shadow.directlyOn(
Settings.Secure.class,
"putIntForUser",
ReflectionHelpers.ClassParameter.from(ContentResolver.class, resolver),
ReflectionHelpers.ClassParameter.from(String.class, name),
ReflectionHelpers.ClassParameter.from(int.class, value),
ReflectionHelpers.ClassParameter.from(int.class, userHandle));
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.content.ComponentName;
import android.content.Context;
import com.android.internal.telephony.SmsApplication;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(SmsApplication.class)
public class ShadowSmsApplication {
private static ComponentName sDefaultSmsApplication;
@Resetter
public static void reset() {
sDefaultSmsApplication = null;
}
@Implementation
protected static ComponentName getDefaultSmsApplication(
Context context, boolean updateIfNeeded) {
return sDefaultSmsApplication;
}
public static void setDefaultSmsApplication(ComponentName defaultSmsApplication) {
sDefaultSmsApplication = defaultSmsApplication;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 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.car.settings.testutils;
import android.telephony.SmsManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(SmsManager.class)
public class ShadowSmsManager {
private static SmsManager sSmsManager;
public static void setDefault(SmsManager smsManager) {
sSmsManager = smsManager;
}
@Resetter
public static void reset() {
sSmsManager = null;
}
@Implementation
public static SmsManager getDefault() {
return sSmsManager;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2019 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.car.settings.testutils;
import android.os.storage.VolumeInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(StorageManagerVolumeProvider.class)
public class ShadowStorageManagerVolumeProvider {
private static VolumeInfo sVolumeInfo;
@Resetter
public static void reset() {
sVolumeInfo = null;
}
@Implementation
protected VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
return sVolumeInfo;
}
public static void setVolumeInfo(VolumeInfo volumeInfo) {
sVolumeInfo = volumeInfo;
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2019 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.car.settings.testutils;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.SubscriptionPlan;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.List;
@Implements(SubscriptionManager.class)
public class ShadowSubscriptionManager extends org.robolectric.shadows.ShadowSubscriptionManager {
private static SubscriptionInfo sDefaultDataSubscriptionInfo = null;
private List<SubscriptionPlan> mSubscriptionPlanList;
private List<SubscriptionInfo> mSelectableSubscriptionInfoList;
private List<OnSubscriptionsChangedListener> mOnSubscriptionsChangedListeners =
new ArrayList<>();
private int mCurrentActiveSubscriptionId;
@Implementation
protected List<SubscriptionPlan> getSubscriptionPlans(int subId) {
return mSubscriptionPlanList;
}
public void setSubscriptionPlans(List<SubscriptionPlan> subscriptionPlanList) {
mSubscriptionPlanList = subscriptionPlanList;
}
@Implementation
protected SubscriptionInfo getDefaultDataSubscriptionInfo() {
return sDefaultDataSubscriptionInfo;
}
public static void setDefaultDataSubscriptionInfo(SubscriptionInfo subscriptionInfo) {
sDefaultDataSubscriptionInfo = subscriptionInfo;
}
@Implementation
protected List<SubscriptionInfo> getSelectableSubscriptionInfoList() {
return mSelectableSubscriptionInfoList;
}
public void setSelectableSubscriptionInfoList(List<SubscriptionInfo> infos) {
mSelectableSubscriptionInfoList = infos;
}
@Implementation
protected void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
super.addOnSubscriptionsChangedListener(listener);
mOnSubscriptionsChangedListeners.add(listener);
}
@Implementation
protected void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
super.removeOnSubscriptionsChangedListener(listener);
mOnSubscriptionsChangedListeners.remove(listener);
}
public List<OnSubscriptionsChangedListener> getOnSubscriptionChangedListeners() {
return mOnSubscriptionsChangedListeners;
}
@Implementation
protected boolean isActiveSubscriptionId(int subscriptionId) {
return mCurrentActiveSubscriptionId == subscriptionId;
}
public void setCurrentActiveSubscriptionId(int subscriptionId) {
mCurrentActiveSubscriptionId = subscriptionId;
}
@Resetter
public static void reset() {
org.robolectric.shadows.ShadowSubscriptionManager.reset();
sDefaultDataSubscriptionInfo = null;
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2018 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.car.settings.testutils;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Implements(TelephonyManager.class)
public class ShadowTelephonyManager extends org.robolectric.shadows.ShadowTelephonyManager {
public static final String SUBSCRIBER_ID = "test_id";
private static Map<Integer, Integer> sSubIdsWithResetCalledCount = new HashMap<>();
private static int sSimCount = 1;
private final Map<PhoneStateListener, Integer> mPhoneStateRegistrations = new HashMap<>();
private boolean mIsDataEnabled = false;
private boolean mIsRoamingEnabled = false;
public static boolean verifyFactoryResetCalled(int subId, int numTimes) {
if (!sSubIdsWithResetCalledCount.containsKey(subId)) return false;
return sSubIdsWithResetCalledCount.get(subId) == numTimes;
}
@Implementation
protected void listen(PhoneStateListener listener, int flags) {
super.listen(listener, flags);
if (flags == LISTEN_NONE) {
mPhoneStateRegistrations.remove(listener);
} else {
mPhoneStateRegistrations.put(listener, flags);
}
}
public List<PhoneStateListener> getListenersForFlags(int flags) {
List<PhoneStateListener> listeners = new ArrayList<>();
for (PhoneStateListener listener : mPhoneStateRegistrations.keySet()) {
if ((mPhoneStateRegistrations.get(listener) & flags) != 0) {
listeners.add(listener);
}
}
return listeners;
}
@Implementation
public void setDataEnabled(boolean enable) {
mIsDataEnabled = enable;
}
@Implementation
public boolean isDataEnabled() {
return mIsDataEnabled;
}
@Implementation
protected void factoryReset(int subId) {
sSubIdsWithResetCalledCount.put(subId,
sSubIdsWithResetCalledCount.getOrDefault(subId, 0) + 1);
}
@Implementation
protected String getSubscriberId(int subId) {
return subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID ? null : SUBSCRIBER_ID;
}
@Implementation
protected int getSimCount() {
return sSimCount;
}
public static void setSimCount(int simCount) {
sSimCount = simCount;
}
@Implementation
protected void setDataRoamingEnabled(boolean isEnabled) {
mIsRoamingEnabled = isEnabled;
}
@Implementation
protected boolean isDataRoamingEnabled() {
return mIsRoamingEnabled;
}
@Resetter
public static void reset() {
sSubIdsWithResetCalledCount.clear();
sSimCount = 1;
}
}

Some files were not shown because too many files have changed in this diff Show More