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,43 @@
// 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.
// Car Settings Robolectric test target.
package {
default_team: "trendy_team_system_experience",
default_applicable_licenses: ["Android-Apache-2.0"],
}
android_robolectric_test {
name: "CarSettingsRoboTests",
srcs: [
"src/**/*.java",
],
libs: [
"Settings-robo-testutils",
"truth",
"testng",
"android.car",
"android.car.test.utils",
],
java_resource_dirs: ["config"],
instrumentation_for: "CarSettingsForTesting",
test_options: {
timeout: 36000,
},
upstream: true,
}

View File

@@ -0,0 +1,20 @@
#
# Copyright (C) 2018 Google Inc.
#
# 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.
#
sdk=NEWEST_SDK
shadows=\
com.android.car.settings.testutils.ShadowCar, \
com.android.car.settings.testutils.ShadowTypeface
looperMode=LEGACY

View File

@@ -0,0 +1,6 @@
Unit test suite for SettingsLib using Robolectric.
```
$ croot
$ make RunCarSettingsRoboTests -j96
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<string-array name="entries" translatable="false">
<item>Choose me!</item>
<item>No, me!</item>
<item>What about me?!</item>
</string-array>
<string-array name="entry_values" translatable="false">
<item>alpha</item>
<item>beta</item>
<item>charlie</item>
</string-array>
</resources>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<!-- explicitly set to unset string for testing purpose -->
<string name="config_defaultAssistantComponentName" translatable="false">#+UNSET</string>
<!-- Whether exit button in settings' root action bar should be shown or not -->
<bool name="config_show_settings_root_exit_icon">true</bool>
<string name="config_default_screen_reader" translatable="false">com.android.screen.reader/.ScreenReaderService</string>
</resources>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<bool name="config_automatic_brightness_available">true</bool>
<integer name="config_networkAvoidBadWifi">0</integer>
<!-- Progress bar preference resources -->
<color name="config_progress_background_tint">@android:color/white</color>
<color name="white_disabled_material">@android:color/white</color>
</resources>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<!-- Namespaced with "tpk" (Test Preference Key) to avoid conflicts. -->
<resources>
<!-- SettingsFragmentTest -->
<string name="tpk_fake_controller" translatable="false">fake_controller</string>
<string name="tpk_edit_text_preference" translatable="false">edit_text_preference</string>
<string name="tpk_list_preference" translatable="false">list_preference</string>
<string name="tpk_two_action_preference" translatable="false">two_action_preference</string>
</resources>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="test_volume_call" translatable="false">Call Volume</string>
<string name="test_volume_music" translatable="false">Music Volume</string>
<!-- AccountListPreferenceControllerTest, AddAccountPreferenceControllerTest, and AccountSettingsFragmentTest -->
<string name="account_type1_label" translatable="false">Type 1</string>
<string name="account_type2_label" translatable="false">Type 2</string>
<string name="account_type3_label" translatable="false">Type 3</string>
<string name="bt_rename_dialog_title" translatable="false">Rename Title</string>
<string name="bt_profile_name" translatable="false">Profile Name</string>
<string name="test_positive_button_label" translatable="false">Positive Button</string>
<string name="test_negative_button_label" translatable="false">Negative Button</string>
<string name="test_neutral_button_label" translatable="false">Neutral Button</string>
<string name="fake_title" translatable="false">fake_title</string>
<string name="fake_summary" translatable="false">fake_summary</string>
<string name="fake_category" translatable="false">fake_category</string>
</resources>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
<Preference
android:key="key"
settings:controller="com.android.car.settings.common.NonExistentPreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
<Preference
android:key="key1"
settings:occupant_driver="write"
settings:occupant_front_passenger="read"
settings:occupant_rear_passenger="hidden"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
<Preference
android:key="key2"
settings:occupant_driver="hide"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen xmlns:settings="http://schemas.android.com/apk/res-auto">
<Preference
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
<Preference
android:key="key1"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
<Preference
android:key="key2"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
<!-- Preferences without controllers should be skipped by the list helper. -->
<Preference
android:key="key3"/>
</PreferenceScreen>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
<Preference
android:key="key1"
settings:occupant_driver="write"
settings:occupant_front_passenger="read"
settings:occupant_rear_passenger="hidden"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
<Preference
android:key="key2"
settings:occupant_driver="hidden"
settings:occupant_front_passenger="write"
settings:occupant_rear_passenger="read"
settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="fake_title_key"
android:title="screen_title"
settings:controller="com.android.car.settings.common.FakePreferenceController">
<Preference
android:key="key1"
settings:occupant_driver="write"
settings:occupant_front_passenger="read"
settings:occupant_rear_passenger="hidden"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
<PreferenceCategory
android:key="key2"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="fake_title_key"
android:title="screen_title"
settings:controller="com.android.car.settings.common.FakePreferenceController">
<Preference
android:key="key1"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
<PreferenceCategory
android:key="key2"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
<com.android.car.settings.common.LogicalPreferenceGroup
android:key="key3"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="screen_title">
<Preference
android:key="@string/tpk_fake_controller"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
<EditTextPreference
android:key="@string/tpk_edit_text_preference"/>
<ListPreference
android:title="@string/tpk_list_preference"
android:dialogTitle="@string/tpk_list_preference"
android:entries="@array/entries"
android:entryValues="@array/entry_values"
android:key="@string/tpk_list_preference"/>
</PreferenceScreen>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<!--
We use a separate test file due to the difficulty accessing android internal resources in tests.
-->
<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
<item car:usage="voice_communication"
car:titleText="@string/test_volume_call"
car:icon="@drawable/test_icon"/>
<item car:usage="voice_communication_signalling"
car:titleText="@string/test_volume_call"
car:icon="@drawable/test_icon"/>
<item car:usage="media"
car:titleText="@string/test_volume_music"
car:icon="@drawable/test_icon"/>
</carVolumeItems>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="screen_title">
<Preference
android:key="@string/tpk_fake_controller"
settings:controller="com.android.car.settings.common.FakePreferenceController"/>
</PreferenceScreen>

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);
}
}

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