fix: 首次提交

This commit is contained in:
2024-12-09 11:25:23 +08:00
parent d0c01071e9
commit 2c2109a5f3
4741 changed files with 290641 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
// Copyright (C) 2015 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "SettingsLibTests",
use_resource_processor: true,
defaults: [
"SettingsLibDefaults",
"framework-wifi-test-defaults",
],
certificate: "platform",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
libs: [
"android.test.runner",
"telephony-common",
"android.test.base",
"android.test.mock",
"truth",
],
platform_apis: true,
test_suites: ["device-tests"],
static_libs: [
"androidx.test.core",
"androidx.test.rules",
"androidx.test.espresso.core",
"androidx.test.ext.junit",
"flag-junit",
"kotlinx_coroutines_test",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"truth",
"SettingsLibDeviceStateRotationLock",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
"settingslib_media_flags_lib",
],
jni_libs: [
"libdexmakerjvmtiagent",
"libmultiplejvmtiagentsinterferenceagent",
"libstaticjvmtiagent",
],
dxflags: ["--multi-dex"],
manifest: "AndroidManifest.xml",
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.tests.integ">
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:debuggable="true" android:testOnly="true">
<uses-library android:name="android.test.runner" />
<activity android:name=".drawer.SettingsDrawerActivityTest$TestActivity"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.settingslib.test"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settingslib.tests.integ"
android:label="Tests for SettingsLib">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Runs Tests for SettingsLib.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="SettingsLibTests.apk" />
<option name="install-arg" value="-t" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<option name="test-tag" value="SettingsLibTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.settingslib.tests.integ" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

View File

@@ -0,0 +1,20 @@
<!--
~ 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.
-->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Offer access to files under Context.getCacheDir() -->
<cache-path name="my_cache" />
</paths>

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 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.settingslib;
import android.test.AndroidTestCase;
public class BaseTest extends AndroidTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
// Mockito stuff.
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
}
}

View File

@@ -0,0 +1,344 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
import android.os.Flags;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ApplicationsStateTest {
private static final int APP_ENTRY_ID = 1;
private ApplicationsState.AppEntry mEntry;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
mEntry = new ApplicationsState.AppEntry(
ApplicationProvider.getApplicationContext(),
mock(ApplicationInfo.class),
APP_ENTRY_ID);
}
@Test
public void testGamesFilterAcceptsGameDeprecated() {
mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
assertThat(ApplicationsState.FILTER_GAMES.filterApp(mEntry)).isTrue();
}
@Test
public void testGameFilterAcceptsCategorizedGame() {
mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
assertThat(ApplicationsState.FILTER_GAMES.filterApp(mEntry)).isTrue();
}
@Test
public void testGameFilterAcceptsCategorizedGameAndDeprecatedIsGame() {
mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
assertThat(ApplicationsState.FILTER_GAMES.filterApp(mEntry)).isTrue();
}
@Test
public void testGamesFilterRejectsNotGame() {
mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED;
assertThat(ApplicationsState.FILTER_GAMES.filterApp(mEntry)).isFalse();
}
@Test
public void testAudioFilterAcceptsCategorizedAudio() {
mEntry.info.category = ApplicationInfo.CATEGORY_AUDIO;
assertThat(ApplicationsState.FILTER_AUDIO.filterApp(mEntry)).isTrue();
}
@Test
public void testAudiosFilterRejectsNotAudio() {
mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
assertThat(ApplicationsState.FILTER_AUDIO.filterApp(mEntry)).isFalse();
}
@Test
public void testAudiosFilterRejectsDefaultCategory() {
mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED;
assertThat(ApplicationsState.FILTER_AUDIO.filterApp(mEntry)).isFalse();
}
@Test
public void testOtherAppsRejectsAudio() {
mEntry.info.category = ApplicationInfo.CATEGORY_AUDIO;
assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
}
@Test
public void testOtherAppsRejectsGame() {
mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
}
@Test
public void testOtherAppsRejectsImageApp() {
mEntry.info.category = ApplicationInfo.CATEGORY_IMAGE;
assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
}
@Test
public void testOtherAppsAcceptsDefaultCategory() {
mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED;
assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isTrue();
}
@Test
public void testPhotosFilterAcceptsFilter() {
mEntry.info.category = ApplicationInfo.CATEGORY_IMAGE;
assertThat(ApplicationsState.FILTER_PHOTOS.filterApp(mEntry)).isTrue();
}
@Test
public void testPhotosFilterRejectsNotPhotos() {
mEntry.info.category = ApplicationInfo.CATEGORY_VIDEO;
assertThat(ApplicationsState.FILTER_PHOTOS.filterApp(mEntry)).isFalse();
}
@Test
public void testPhotosFilterRejectsDefaultCategory() {
mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED;
assertThat(ApplicationsState.FILTER_PHOTOS.filterApp(mEntry)).isFalse();
}
@Test
public void testAppsExceptGamesFilterRejectsGame() {
mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isFalse();
}
@Test
public void testAppsExceptGamesFilterAcceptsImage() {
mEntry.info.category = ApplicationInfo.CATEGORY_IMAGE;
assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isTrue();
}
@Test
public void testAppsExceptGamesFilterAcceptsVideo() {
mEntry.info.category = ApplicationInfo.CATEGORY_VIDEO;
assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isTrue();
}
@Test
public void testAppsExceptGamesFilterAcceptsAudio() {
mEntry.info.category = ApplicationInfo.CATEGORY_AUDIO;
assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isTrue();
}
@Test
public void testDownloadAndLauncherAndInstantAcceptsCorrectApps() {
// should include instant apps
mEntry.isHomeApp = false;
mEntry.hasLauncherEntry = false;
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry))
.isTrue();
// should included updated system apps
when(mEntry.info.isInstantApp()).thenReturn(false);
mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry))
.isTrue();
// should not include system apps other than the home app
mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM;
mEntry.isHomeApp = false;
mEntry.hasLauncherEntry = false;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry))
.isFalse();
// should include the home app
mEntry.isHomeApp = true;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry))
.isTrue();
// should include any System app with a launcher entry
mEntry.isHomeApp = false;
mEntry.hasLauncherEntry = true;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT.filterApp(mEntry))
.isTrue();
}
@Test
public void testDownloadAndLauncherAcceptsCorrectApps() {
mEntry.isHomeApp = false;
mEntry.hasLauncherEntry = false;
// should included updated system apps
when(mEntry.info.isInstantApp()).thenReturn(false);
mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry))
.isTrue();
// should not include system apps other than the home app
mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM;
mEntry.isHomeApp = false;
mEntry.hasLauncherEntry = false;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry))
.isFalse();
// should include the home app
mEntry.isHomeApp = true;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry))
.isTrue();
// should include any System app with a launcher entry
mEntry.isHomeApp = false;
mEntry.hasLauncherEntry = true;
assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(mEntry))
.isTrue();
}
@Test
public void testOtherAppsRejectsLegacyGame() {
mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
}
@Test
public void testInstantFilterAcceptsInstantApp() {
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_INSTANT.filterApp(mEntry)).isTrue();
}
@Test
public void testInstantFilterRejectsNonInstantApp() {
when(mEntry.info.isInstantApp()).thenReturn(false);
assertThat(ApplicationsState.FILTER_INSTANT.filterApp(mEntry)).isFalse();
}
@Test
public void testEnabledFilterRejectsInstantApp() {
mEntry.info.enabled = true;
assertThat(ApplicationsState.FILTER_ALL_ENABLED.filterApp(mEntry)).isTrue();
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_ALL_ENABLED.filterApp(mEntry)).isFalse();
}
@Test
public void testFilterWithDomainUrls() {
mEntry.info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
// should included updated system apps
when(mEntry.info.isInstantApp()).thenReturn(false);
assertThat(ApplicationsState.FILTER_WITH_DOMAIN_URLS.filterApp(mEntry))
.isTrue();
mEntry.info.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
assertThat(ApplicationsState.FILTER_WITH_DOMAIN_URLS.filterApp(mEntry))
.isFalse();
mEntry.info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_WITH_DOMAIN_URLS.filterApp(mEntry))
.isFalse();
}
@Test
public void testDisabledFilterRejectsInstantApp() {
mEntry.info.enabled = false;
assertThat(ApplicationsState.FILTER_DISABLED.filterApp(mEntry)).isTrue();
when(mEntry.info.isInstantApp()).thenReturn(true);
assertThat(ApplicationsState.FILTER_DISABLED.filterApp(mEntry)).isFalse();
}
@Test
public void testVideoFilterAcceptsCategorizedVideo() {
mEntry.info.category = ApplicationInfo.CATEGORY_VIDEO;
assertThat(ApplicationsState.FILTER_MOVIES.filterApp(mEntry)).isTrue();
}
@Test
public void testVideosFilterRejectsNotVideo() {
mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
assertThat(ApplicationsState.FILTER_MOVIES.filterApp(mEntry)).isFalse();
}
@Test
public void testPersonalAndWorkFiltersDisplaysCorrectApps() {
mEntry.showInPersonalTab = true;
mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
assertThat(ApplicationsState.FILTER_WORK.filterApp(mEntry)).isFalse();
mEntry.showInPersonalTab = false;
mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_MANAGED;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
assertThat(ApplicationsState.FILTER_WORK.filterApp(mEntry)).isTrue();
}
@Test
public void testPrivateProfileFilterDisplaysCorrectApps() {
mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
mEntry.showInPersonalTab = true;
mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse();
mEntry.showInPersonalTab = false;
mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isTrue();
}
@Test
public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() {
mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
mEntry.showInPersonalTab = false;
mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import android.app.usage.StorageStats;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class StorageStatsSourceTest {
@Test
public void AppStorageStatsImpl_totalCorrectly() {
StorageStats storageStats = new StorageStats();
storageStats.cacheBytes = 1;
storageStats.codeBytes = 10;
storageStats.dataBytes = 100;
StorageStatsSource.AppStorageStatsImpl stats = new StorageStatsSource.AppStorageStatsImpl(
storageStats);
// Note that this does not double add the cache (111).
assertThat(stats.getTotalBytes()).isEqualTo(110);
}
}

View File

@@ -0,0 +1,130 @@
/*
* 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.settingslib.bluetooth;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.concurrent.CountDownLatch;
/**
* Test that verifies that BluetoothEventManager can receive broadcasts for non-current
* users for all bluetooth events.
*
* <p>Creation and deletion of users takes a long time, so marking this as a LargeTest.
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
public class BluetoothEventManagerIntegTest {
private static final int LATCH_TIMEOUT = 4;
private Context mContext;
private UserManager mUserManager;
private BluetoothEventManager mBluetoothEventManager;
private UserInfo mOtherUser;
private final Intent mTestIntent = new Intent("Test intent");
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mUserManager = UserManager.get(mContext);
mBluetoothEventManager = new BluetoothEventManager(
mock(LocalBluetoothAdapter.class), mock(CachedBluetoothDeviceManager.class),
mContext, /* handler= */ null, UserHandle.ALL);
// Create and start another user in the background.
mOtherUser = mUserManager.createUser("TestUser", /* flags= */ 0);
try {
ActivityManager.getService().startUserInBackground(mOtherUser.id);
} catch (RemoteException e) {
fail("Count't create an additional user.");
}
}
@After
public void tearDown() {
if (mOtherUser != null) {
mUserManager.removeUser(mOtherUser.id);
}
}
/**
* Verify that MultiUserAwareBluetoothEventManager's adapter receiver handles events coming from
* users other than current user.
*/
@Test
public void registerAdapterReceiver_ifIntentFromAnotherUser_broadcastIsReceived()
throws Exception {
// Create a latch to listen for the intent.
final CountDownLatch broadcastLatch = new CountDownLatch(1);
// Register adapter receiver.
mBluetoothEventManager.addHandler(mTestIntent.getAction(),
(context, intent, device) -> broadcastLatch.countDown());
mBluetoothEventManager.registerAdapterIntentReceiver();
// Send broadcast from another user.
mContext.sendBroadcastAsUser(mTestIntent, mOtherUser.getUserHandle());
// Wait to receive it.
assertTrue(broadcastLatch.await(LATCH_TIMEOUT, SECONDS));
}
/**
* Verify that MultiUserAwareBluetoothEventManager's profile receiver handles events coming from
* users other than current user.
*/
@Test
public void registerProfileReceiver_ifIntentFromAnotherUser_broadcastIsReceived()
throws Exception {
// Create a latch to listen for the intent.
final CountDownLatch broadcastLatch = new CountDownLatch(1);
// Register profile receiver.
mBluetoothEventManager.addProfileHandler(mTestIntent.getAction(),
(context, intent, device) -> broadcastLatch.countDown());
mBluetoothEventManager.registerProfileIntentReceiver();
// Send broadcast from another user.
mContext.sendBroadcastAsUser(mTestIntent, mOtherUser.getUserHandle());
// Wait to receive it.
assertTrue(broadcastLatch.await(LATCH_TIMEOUT, SECONDS));
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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.settingslib.devicestate;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
import com.google.common.truth.Expect;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DeviceStateRotationLockSettingsManagerTest {
@Rule public Expect mExpect = Expect.create();
@Mock private Context mMockContext;
@Mock private Resources mMockResources;
private DeviceStateRotationLockSettingsManager mManager;
private int mNumSettingsChanges = 0;
private final ContentObserver mContentObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
mNumSettingsChanges++;
}
};
private final FakeSecureSettings mFakeSecureSettings = new FakeSecureSettings();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver());
when(mMockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
.thenReturn(new String[]{"0:1", "1:0:2", "2:2"});
when(mMockResources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(new int[]{0});
when(mMockResources.getIntArray(R.array.config_halfFoldedDeviceStates))
.thenReturn(new int[]{1});
when(mMockResources.getIntArray(R.array.config_openDeviceStates))
.thenReturn(new int[]{2});
mFakeSecureSettings.registerContentObserver(
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
/* notifyForDescendents= */ false, //NOTYPO
mContentObserver,
UserHandle.USER_CURRENT);
mManager = new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);
}
@Test
public void initialization_settingsAreChangedOnce() {
assertThat(mNumSettingsChanges).isEqualTo(1);
}
@Test
public void updateSetting_multipleTimes_sameValue_settingsAreChangedOnlyOnce() {
mNumSettingsChanges = 0;
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
assertThat(mNumSettingsChanges).isEqualTo(1);
}
@Test
public void updateSetting_multipleTimes_differentValues_settingsAreChangedMultipleTimes() {
mNumSettingsChanges = 0;
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ false);
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
assertThat(mNumSettingsChanges).isEqualTo(3);
}
@Test
public void updateSetting_twiceWithSameValue_persistedValueDifferent_persistsAgain() {
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
// This persists a different setting than what was set above. It simulates the persisted
// setting being changed from a different process.
persistSettings("0:1:1:2:2:2");
mNumSettingsChanges = 0;
// Updating again with the same value as in the first line of the test should persist the
// setting, as it is different to what is actually persisted.
mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
assertThat(mNumSettingsChanges).isEqualTo(1);
}
@Test
public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
when(mMockResources.getStringArray(
R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
new String[]{"2:1", "1:0:1", "0:2"});
List<SettableDeviceState> settableDeviceStates =
DeviceStateRotationLockSettingsManager.getInstance(
mMockContext).getSettableDeviceStates();
assertThat(settableDeviceStates).containsExactly(
new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ false),
new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ true)
).inOrder();
}
@Test
public void persistedInvalidIgnoredState_returnsDefaults() {
when(mMockResources.getStringArray(
R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
new String[]{"0:1", "1:0:2", "2:2"});
// Here 2 has IGNORED, and in the defaults 1 has IGNORED.
persistSettings("0:2:2:0:1:2");
DeviceStateRotationLockSettingsManager manager =
new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);
mExpect.that(manager.getRotationLockSetting(0)).isEqualTo(1);
mExpect.that(manager.getRotationLockSetting(1)).isEqualTo(2);
mExpect.that(manager.getRotationLockSetting(2)).isEqualTo(2);
}
@Test
public void persistedValidValues_returnsPersistedValues() {
when(mMockResources.getStringArray(
R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
new String[]{"0:1", "1:0:2", "2:2"});
persistSettings("0:2:1:0:2:1");
DeviceStateRotationLockSettingsManager manager =
new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);
mExpect.that(manager.getRotationLockSetting(0)).isEqualTo(2);
mExpect.that(manager.getRotationLockSetting(1)).isEqualTo(1);
mExpect.that(manager.getRotationLockSetting(2)).isEqualTo(1);
}
private void persistSettings(String value) {
mFakeSecureSettings.putStringForUser(
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
value,
UserHandle.USER_CURRENT);
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.settingslib.devicestate;
import android.database.ContentObserver;
import android.util.Pair;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.util.HashMap;
import java.util.Map;
/** Fake implementation of {@link SecureSettings} that stores everything in memory. */
class FakeSecureSettings implements SecureSettings {
private final Map<SettingsKey, String> mValues = new HashMap<>();
private final Multimap<SettingsKey, ContentObserver> mContentObservers = HashMultimap.create();
@Override
public void putStringForUser(String name, String value, int userHandle) {
SettingsKey settingsKey = new SettingsKey(userHandle, name);
mValues.put(settingsKey, value);
for (ContentObserver observer : mContentObservers.get(settingsKey)) {
observer.onChange(/* selfChange= */ false);
}
}
@Override
public String getStringForUser(String name, int userHandle) {
return mValues.getOrDefault(new SettingsKey(userHandle, name), "");
}
@Override
public void registerContentObserver(String name, boolean notifyForDescendants,
ContentObserver settingsObserver, int userHandle) {
mContentObservers.put(new SettingsKey(userHandle, name), settingsObserver);
}
private static class SettingsKey extends Pair<Integer, String> {
SettingsKey(Integer userHandle, String settingName) {
super(userHandle, settingName);
}
}
}

View File

@@ -0,0 +1,3 @@
# Default reviewers for this and subdirectories.
alexflo@google.com
chrisgollner@google.com

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 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.settingslib.devicestate
import android.content.Context
import android.content.res.Resources
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.internal.R
import com.google.common.truth.Expect
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
private const val DEVICE_STATE_UNKNOWN = 0
private const val DEVICE_STATE_CLOSED = 1
private const val DEVICE_STATE_HALF_FOLDED = 2
private const val DEVICE_STATE_OPEN = 3
private const val DEVICE_STATE_REAR_DISPLAY = 4
@SmallTest
@RunWith(AndroidJUnit4::class)
class PosturesHelperTest {
@get:Rule val expect: Expect = Expect.create()
@Mock private lateinit var context: Context
@Mock private lateinit var resources: Resources
private lateinit var posturesHelper: PosturesHelper
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(context.resources).thenReturn(resources)
whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(intArrayOf(DEVICE_STATE_CLOSED))
whenever(resources.getIntArray(R.array.config_halfFoldedDeviceStates))
.thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED))
whenever(resources.getIntArray(R.array.config_openDeviceStates))
.thenReturn(intArrayOf(DEVICE_STATE_OPEN))
whenever(resources.getIntArray(R.array.config_rearDisplayDeviceStates))
.thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY))
posturesHelper = PosturesHelper(context)
}
@Test
fun deviceStateToPosture_mapsCorrectly() {
expect
.that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED))
.isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED)
expect
.that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
.isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
expect
.that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN))
.isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
expect
.that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
.isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
expect
.that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN))
.isEqualTo(DEVICE_STATE_ROTATION_KEY_UNKNOWN)
}
@Test
fun postureToDeviceState_mapsCorrectly() {
expect
.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED))
.isEqualTo(DEVICE_STATE_CLOSED)
expect
.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED))
.isEqualTo(DEVICE_STATE_HALF_FOLDED)
expect
.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED))
.isEqualTo(DEVICE_STATE_OPEN)
expect
.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY))
.isEqualTo(DEVICE_STATE_REAR_DISPLAY)
expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull()
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.drawable;
import static com.google.common.truth.Truth.assertThat;
import android.annotation.ColorInt;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class UserIconDrawableTest {
private UserIconDrawable mDrawable;
@Test
public void getConstantState_shouldNotBeNull() {
final Bitmap b = BitmapFactory.decodeResource(
InstrumentationRegistry.getTargetContext().getResources(),
com.android.internal.R.drawable.ic_mode_edit);
mDrawable = new UserIconDrawable(100 /* size */).setIcon(b).bake();
assertThat(mDrawable.getConstantState()).isNotNull();
}
@Test
public void setTintList_shouldBeApplied() {
@ColorInt final int targetColor = Color.BLUE;
final PorterDuff.Mode mode = Mode.SRC_OVER;
final Bitmap b = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
UserIconDrawable drawable = new UserIconDrawable().setIcon(b);
drawable.setBounds(0, 0, 100, 100);
int[][] stateSet = new int[][] { {} };
int[] colors = new int[] { targetColor };
drawable.setTintList(new ColorStateList(stateSet, colors));
drawable.setTintMode(mode);
Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.draw(canvas);
assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.BLUE);
}
}

View File

@@ -0,0 +1,108 @@
package com.android.settingslib.graph;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryMeterDrawableBaseTest {
private Context mContext;
private Resources mResources;
private BatteryMeterDrawableBase mBatteryDrawable;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mResources = mContext.getResources();
mBatteryDrawable = new BatteryMeterDrawableBase(mContext, 0);
}
@Test
public void testGetIntrinsicSize() {
assertThat(mBatteryDrawable.getIntrinsicWidth()).
isEqualTo(mResources.getDimensionPixelSize(R.dimen.battery_width));
assertThat(mBatteryDrawable.getIntrinsicHeight()).
isEqualTo(mResources.getDimensionPixelSize(R.dimen.battery_height));
}
@Test
public void testDrawNothingBeforeOnBatteryLevelChanged() {
final Canvas canvas = mock(Canvas.class);
mBatteryDrawable.draw(canvas);
verify(canvas, never()).drawPath(any(), any());
verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
}
@Test
public void testDrawingForTypicalValues() {
final Canvas canvas = mock(Canvas.class);
final int levels[] = { 0, 1, 5, 10, 25, 50, 75, 90, 95, 99, 100 };
final boolean bools[] = { false, true };
for (int l : levels) {
for (boolean charging : bools) {
for (boolean saver : bools) {
for (boolean percent : bools) {
mBatteryDrawable.setBatteryLevel(l);
mBatteryDrawable.setPowerSave(saver);
mBatteryDrawable.setCharging(charging);
mBatteryDrawable.setShowPercent(percent);
mBatteryDrawable.draw(canvas);
}
}
}
}
}
@Test
public void testPadding_returnsCorrectValues() {
// different pads on each side to differentiate
final int left = 1;
final int top = 2;
final int right = 3;
final int bottom = 4;
final Rect expected = new Rect(left, top, right, bottom);
final Rect padding = new Rect();
mBatteryDrawable.setPadding(left, top, right, bottom);
assertThat(mBatteryDrawable.getPadding(padding)).isEqualTo(true);
assertThat(padding).isEqualTo(expected);
}
@Test
public void testPadding_falseIfUnsetOrZero() {
final Rect padding = new Rect();
assertThat(mBatteryDrawable.getPadding(padding)).isEqualTo(false);
assertThat(isRectZero(padding)).isEqualTo(true);
mBatteryDrawable.setPadding(0, 0, 0, 0);
assertThat(mBatteryDrawable.getPadding(padding)).isEqualTo(false);
assertThat(isRectZero(padding)).isEqualTo(true);
}
private boolean isRectZero(Rect r) {
return r.left == 0 && r.top == 0 && r.right == 0 && r.bottom == 0;
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.inputmethod;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntBiFunction;
/**
* Utility class to assert {@link Comparable} objects.
*/
final class ComparableUtils {
private ComparableUtils() {
}
/**
* Checks whether specified items have ascending ordering.
*
* @param items objects to be checked Comparable contracts.
* @param compareTo function to compare two objects as {@link Comparable#compareTo(Object)}.
* @param name function to extract name of an object.
* @param <T> type that implements {@link Comparable}.
*/
static <T> void assertAscendingOrdering(final List<T> items,
final ToIntBiFunction<T, T> compareTo, final Function<T, String> name) {
for (int i = 1; i < items.size(); i++) {
final T x = items.get(i - 1);
final T y = items.get(i);
assertTrue(name.apply(x) + " is less than " + name.apply(y),
compareTo.applyAsInt(x, y) < 0);
}
}
/**
* Checks whether specified items have the same ordering.
*
* @param items objects to be checked equality.
* @param compareTo function to compare two objects as {@link Comparable#compareTo(Object)}.
* @param name function to extract name of an object.
* @param <T> type that implements {@link Comparable}.
*/
static <T> void assertSameOrdering(final Collection<T> items,
final ToIntBiFunction<T, T> compareTo, final Function<T, String> name) {
for (final T x : items) {
for (final T y : items) {
assertTrue(name.apply(x) + " is equal to " + name.apply(y),
compareTo.applyAsInt(x, y) == 0);
}
}
}
/**
* Checks whether a {@link Comparable} type complies with Comparable contracts.
* <ul>
* <li>Ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y.</li>
* <li>Ensure that the relation is transitive:
* (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.</li>
* <li>Ensure that x.compareTo(y)==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)),
* for all z.</li>
* </ul>
*
* @param items objects to be checked Comparable contracts.
* @param compareTo function to compare two objects as {@link Comparable#compareTo(Object)}.
* @param name function to extract name of an object.
* @param <T> type that implements {@link Comparable}.
*/
static <T> void assertComparableContracts(final Collection<T> items,
final ToIntBiFunction<T, T> compareTo, final Function<T, String> name) {
for (final T x : items) {
final String nameX = name.apply(x);
assertTrue("Reflective: " + nameX + " is equal to itself",
compareTo.applyAsInt(x, x) == 0);
for (final T y : items) {
final String nameY = name.apply(y);
assertEquals("Asymmetric: " + nameX + " and " + nameY,
Integer.signum(compareTo.applyAsInt(x, y)),
-Integer.signum(compareTo.applyAsInt(y, x)));
for (final T z : items) {
final String nameZ = name.apply(z);
if (compareTo.applyAsInt(x, y) > 0 && compareTo.applyAsInt(y, z) > 0) {
assertTrue("Transitive: " + nameX + " is greater than " + nameY
+ " and " + nameY + " is greater than " + nameZ
+ " then " + nameX + " is greater than " + nameZ,
compareTo.applyAsInt(x, z) > 0);
}
if (compareTo.applyAsInt(x, y) == 0) {
assertEquals("Transitive: " + nameX + " and " + nameY + " is same "
+ " then both return the same result for " + nameZ,
Integer.signum(compareTo.applyAsInt(x, z)),
Integer.signum(compareTo.applyAsInt(y, z)));
}
}
}
}
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.inputmethod;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.UserHandle;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodPreferenceTest {
private static final Collator COLLATOR = Collator.getInstance(Locale.US);
@Test
public void testComparableOrdering() throws Exception {
final List<InputMethodPreference> itemsInAscendingOrder = Arrays.asList(
createPreference("", true, "no_title-system"),
createPreference("E", true, "E-system"),
createPreference("Z", true, "Z-system"),
createPreference("", false, "no_title-non_system"),
createPreference("E", false, "E-non_system"),
createPreference("Z", false, "Z-non_system")
);
ComparableUtils.assertAscendingOrdering(
itemsInAscendingOrder,
(x, y) -> x.compareTo(y, COLLATOR),
x -> x.getInputMethodInfo().getServiceName());
}
@Test
public void testComparableEquality() {
final List<InputMethodPreference> itemsInSameOrder1 = Arrays.asList(
createPreference("", true, "no_title-system-1"),
createPreference("", true, "no_title-system-2")
);
ComparableUtils.assertSameOrdering(
itemsInSameOrder1,
(x, y) -> x.compareTo(y, COLLATOR),
x -> x.getInputMethodInfo().getServiceName());
final List<InputMethodPreference> itemsInSameOrder2 = Arrays.asList(
createPreference("A", false, "A-non_system-1"),
createPreference("A", false, "A-non_system-2")
);
ComparableUtils.assertSameOrdering(
itemsInSameOrder2,
(x, y) -> x.compareTo(y, COLLATOR),
x -> x.getInputMethodInfo().getServiceName());
}
@Test
public void testComparableContracts() {
final List<InputMethodPreference> items = Arrays.asList(
// itemsInAscendingOrder.
createPreference("", true, "no_title-system"),
createPreference("E", true, "E-system"),
createPreference("Z", true, "Z-system"),
createPreference("", false, "no_title-non_system"),
createPreference("E", false, "E-non_system"),
createPreference("Z", false, "Z-non_system"),
// itemsInSameOrder1.
createPreference("", true, "no_title-system-1"),
createPreference("", true, "no_title-system-2"),
// itemsInSameOrder2.
createPreference("A", false, "A-non_system-1"),
createPreference("A", false, "A-non_system-2")
);
ComparableUtils.assertComparableContracts(
items,
(x, y) -> x.compareTo(y, COLLATOR),
x -> x.getInputMethodInfo().getServiceName());
}
private static InputMethodPreference createPreference(
final CharSequence title,
final boolean systemIme,
final String name) {
return new InputMethodPreference(
InstrumentationRegistry.getTargetContext(),
createInputMethodInfo(systemIme, name),
title,
true /* isAllowedByOrganization */,
p -> {} /* onSavePreferenceListener */,
UserHandle.myUserId());
}
private static InputMethodInfo createInputMethodInfo(
final boolean systemIme, final String name) {
final Context targetContext = InstrumentationRegistry.getTargetContext();
final Locale systemLocale = targetContext
.getResources()
.getConfiguration()
.getLocales()
.get(0);
final InputMethodSubtype systemLocaleSubtype =
new InputMethodSubtype.InputMethodSubtypeBuilder()
.setIsAsciiCapable(true)
.setSubtypeMode("keyboard")
.setSubtypeLocale(systemLocale.getLanguage())
.build();
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo.packageName = "com.android.ime";
resolveInfo.serviceInfo.name = name;
resolveInfo.serviceInfo.applicationInfo = new ApplicationInfo();
resolveInfo.serviceInfo.applicationInfo.enabled = true;
if (systemIme) {
resolveInfo.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
} else {
resolveInfo.serviceInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
}
return new InputMethodInfo(
resolveInfo,
false /* isAuxIme */,
"SettingsActivity",
Collections.singletonList(systemLocaleSubtype),
0 /* isDefaultResId */,
true /* forceDefault */);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.inputmethod;
import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodSubtypePreferenceTest {
private static final List<InputMethodSubtypePreference> ITEMS_IN_ASCENDING = Arrays.asList(
// Subtypes that has the same locale of the system's.
createPreference("", "en_US", Locale.US),
createPreference("E", "en_US", Locale.US),
createPreference("Z", "en_US", Locale.US),
// Subtypes that has the same language of the system's.
createPreference("", "en", Locale.US),
createPreference("E", "en", Locale.US),
createPreference("Z", "en", Locale.US),
// Subtypes that has different language than the system's.
createPreference("", "ja", Locale.US),
createPreference("A", "hi_IN", Locale.US),
createPreference("B", "", Locale.US),
createPreference("E", "ja", Locale.US),
createPreference("Z", "ja", Locale.US)
);
private static final List<InputMethodSubtypePreference> SAME_ORDER_ITEMS = Arrays.asList(
// Subtypes that has different language than the system's.
createPreference("A", "ja_JP", Locale.US),
createPreference("A", "hi_IN", Locale.US),
// Subtypes that has an empty subtype locale string.
createPreference("A", "", Locale.US)
);
private static final Collator COLLATOR = Collator.getInstance(Locale.US);
@Test
public void testComparableOrdering() throws Exception {
ComparableUtils.assertAscendingOrdering(
ITEMS_IN_ASCENDING,
(x, y) -> x.compareTo(y, COLLATOR),
InputMethodSubtypePreference::getKey);
}
@Test
public void testComparableEquality() {
ComparableUtils.assertSameOrdering(
SAME_ORDER_ITEMS,
(x, y) -> x.compareTo(y, COLLATOR),
InputMethodSubtypePreference::getKey);
}
@Test
public void testComparableContracts() {
final Collection<InputMethodSubtypePreference> items = new ArrayList<>();
items.addAll(ITEMS_IN_ASCENDING);
items.addAll(SAME_ORDER_ITEMS);
items.add(createPreference("", "", Locale.US));
items.add(createPreference("A", "en", Locale.US));
items.add(createPreference("A", "en_US", Locale.US));
items.add(createPreference("E", "hi_IN", Locale.US));
items.add(createPreference("E", "en", Locale.US));
items.add(createPreference("Z", "en_US", Locale.US));
ComparableUtils.assertComparableContracts(
items,
(x, y) -> x.compareTo(y, COLLATOR),
InputMethodSubtypePreference::getKey);
}
private static InputMethodSubtypePreference createPreference(
final String subtypeName,
final String subtypeLocaleString,
final Locale systemLocale) {
final String key = subtypeName + "-" + subtypeLocaleString + "-" + systemLocale;
final String subtypeLanguageTag = subtypeLocaleString.replace('_', '-');
final Locale subtypeLocale = TextUtils.isEmpty(subtypeLanguageTag)
? null : Locale.forLanguageTag(subtypeLanguageTag);
return new InputMethodSubtypePreference(
InstrumentationRegistry.getTargetContext(),
key,
subtypeName,
subtypeLocale,
systemLocale);
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2023 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.settingslib.media;
import static com.android.settingslib.media.flags.Flags.FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER;
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
import android.app.UiAutomation;
import android.content.Context;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class InfoMediaManagerIntegTest {
private static final String FAKE_PACKAGE = "FAKE_PACKAGE";
private Context mContext;
private UiAutomation mUiAutomation;
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
public void setUp() {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL);
}
@After
public void tearDown() {
mUiAutomation.dropShellPermissionIdentity();
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_returnsRouterInfoMediaManager() {
InfoMediaManager manager =
InfoMediaManager.createInstance(mContext, mContext.getPackageName(), null, null);
assertThat(manager).isInstanceOf(RouterInfoMediaManager.class);
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_withFakePackage_returnsNoOpInfoMediaManager() {
InfoMediaManager manager =
InfoMediaManager.createInstance(mContext, FAKE_PACKAGE, null, null);
assertThat(manager).isInstanceOf(NoOpInfoMediaManager.class);
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_withNullPackage_returnsRouterInfoMediaManager() {
InfoMediaManager manager = InfoMediaManager.createInstance(mContext, null, null, null);
assertThat(manager).isInstanceOf(RouterInfoMediaManager.class);
}
@Test
@RequiresFlagsDisabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOff_returnsManagerInfoMediaManager() {
InfoMediaManager manager =
InfoMediaManager.createInstance(mContext, mContext.getPackageName(), null, null);
assertThat(manager).isInstanceOf(ManagerInfoMediaManager.class);
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2024 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.settingslib.statusbar.notification.data.repository
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.provider.Settings.Global
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.statusbar.notification.data.model.ZenMode
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class NotificationsSoundPolicyRepositoryTest {
@Mock private lateinit var context: Context
@Mock private lateinit var notificationManager: NotificationManager
@Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
private lateinit var underTest: NotificationsSoundPolicyRepository
private val testScope: TestScope = TestScope()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest =
NotificationsSoundPolicyRepositoryImpl(
context,
notificationManager,
testScope.backgroundScope,
testScope.testScheduler,
)
}
@Test
fun policyChanges_repositoryEmits() {
testScope.runTest {
val values = mutableListOf<NotificationManager.Policy?>()
`when`(notificationManager.notificationPolicy).thenReturn(testPolicy1)
underTest.notificationPolicy.onEach { values.add(it) }.launchIn(backgroundScope)
runCurrent()
`when`(notificationManager.notificationPolicy).thenReturn(testPolicy2)
triggerIntent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
runCurrent()
assertThat(values)
.containsExactlyElementsIn(listOf(null, testPolicy1, testPolicy2))
.inOrder()
}
}
@Test
fun zenModeChanges_repositoryEmits() {
testScope.runTest {
val values = mutableListOf<ZenMode?>()
`when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_OFF)
underTest.zenMode.onEach { values.add(it) }.launchIn(backgroundScope)
runCurrent()
`when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_ALARMS)
triggerIntent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
runCurrent()
assertThat(values)
.containsExactlyElementsIn(
listOf(null, ZenMode(Global.ZEN_MODE_OFF), ZenMode(Global.ZEN_MODE_ALARMS))
)
.inOrder()
}
}
private fun triggerIntent(action: String) {
verify(context).registerReceiver(receiverCaptor.capture(), any())
receiverCaptor.value.onReceive(context, Intent(action))
}
private companion object {
val testPolicy1 =
NotificationManager.Policy(
/* priorityCategories = */ 1,
/* priorityCallSenders =*/ 1,
/* priorityMessageSenders = */ 1,
)
val testPolicy2 =
NotificationManager.Policy(
/* priorityCategories = */ 2,
/* priorityCallSenders =*/ 2,
/* priorityMessageSenders = */ 2,
)
}
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright (C) 2016 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.settingslib.users;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.UserHandle;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import com.android.settingslib.BaseTest;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Tests for AppCopyHelper.
*/
@SmallTest
public class AppCopyingHelperTest extends BaseTest {
private @Mock Context mContext;
private @Mock PackageManager mPm;
private @Mock IPackageManager mIpm;
private final UserHandle mTestUser = UserHandle.of(1111);
private AppCopyHelper mHelper;
private final ArrayList<ApplicationInfo> mCurrUserInstalledAppInfos = new ArrayList<>();
private final ArrayList<ApplicationInfo> mTestUserInstalledAppInfos = new ArrayList<>();
@Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
mHelper = new AppCopyHelper(new TestInjector());
}
public void testFetchAndMergeApps() throws Exception {
// Apps on the current user.
final String[] sysInapplicables = new String[] {"sys.no0, sys.no1"};
final String[] sysLaunchables = new String[] {"sys1", "sys2", "sys3"};
final String[] sysWidgets = new String[] {"sys1", "sys4"};
final String[] downloadeds = new String[] {"app1", "app2"};
addInapplicableSystemApps(sysInapplicables);
addSystemAppsForIntent(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
sysLaunchables);
addSystemAppsForIntent(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE),
sysWidgets);
addDownloadedApps(downloadeds);
when(mPm.getInstalledApplications(anyInt())).thenReturn(mCurrUserInstalledAppInfos);
// Apps on the test user.
final String[] testUserApps =
new String[]{"sys.no0", "sys2", "sys4", "app2", "sys999", "app999"};
addAppsToTestUser(testUserApps);
when(mPm.getInstalledApplicationsAsUser(anyInt(), eq(mTestUser.getIdentifier())))
.thenReturn(mTestUserInstalledAppInfos);
mHelper.fetchAndMergeApps();
final ArraySet<String> notExpectedInVisibleApps = new ArraySet<>();
Collections.addAll(notExpectedInVisibleApps, sysInapplicables);
Collections.addAll(notExpectedInVisibleApps, testUserApps);
final ArraySet<String> expectedInVisibleApps = new ArraySet<>();
Collections.addAll(expectedInVisibleApps, sysLaunchables);
Collections.addAll(expectedInVisibleApps, sysWidgets);
Collections.addAll(expectedInVisibleApps, downloadeds);
expectedInVisibleApps.removeAll(notExpectedInVisibleApps);
for (AppCopyHelper.SelectableAppInfo info : mHelper.getVisibleApps()) {
if (expectedInVisibleApps.contains(info.packageName)) {
expectedInVisibleApps.remove(info.packageName);
} else if (notExpectedInVisibleApps.contains(info.packageName)) {
fail("Package: " + info.packageName + " should not be included in visibleApps");
} else {
fail("Unknown package: " + info.packageName);
}
}
assertEquals("Some expected apps are not included in visibleApps: " + expectedInVisibleApps,
0, expectedInVisibleApps.size());
}
public void testInstallSelectedApps() throws Exception {
final int testUserId = mTestUser.getIdentifier();
mHelper.setPackageSelected("app1", true); // Ultimately true
mHelper.setPackageSelected("app2", true); // Ultimately false
mHelper.setPackageSelected("app3", true); // Ultimately true
mHelper.setPackageSelected("app4", true); // Ultimately true
mHelper.setPackageSelected("app2", false);
mHelper.setPackageSelected("app1", false);
mHelper.setPackageSelected("app1", true);
// app3 is installed but hidden
ApplicationInfo info = new ApplicationInfo();
info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
info.flags |= ApplicationInfo.FLAG_INSTALLED;
when(mIpm.getApplicationInfo(eq("app3"), anyLong(), eq(testUserId)))
.thenReturn(info);
info = new ApplicationInfo();
when(mIpm.getApplicationInfo(eq("app4"), anyLong(), eq(testUserId)))
.thenReturn(info);
mHelper.installSelectedApps();
verify(mIpm, times(1)).installExistingPackageAsUser(
"app1", testUserId,
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
PackageManager.INSTALL_REASON_UNKNOWN, null);
verify(mIpm, times(0)).installExistingPackageAsUser(eq(
"app2"), eq(testUserId),
anyInt(), anyInt(), any());
verify(mIpm, times(0)).installExistingPackageAsUser(eq(
"app3"), eq(testUserId),
anyInt(), anyInt(), any());
verify(mIpm, times(1)).installExistingPackageAsUser(
"app4", testUserId,
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
PackageManager.INSTALL_REASON_UNKNOWN, null);
verify(mIpm, times(0)).setApplicationHiddenSettingAsUser(
eq("app1"), anyBoolean(), eq(testUserId));
verify(mIpm, times(0)).setApplicationHiddenSettingAsUser(
eq("app2"), anyBoolean(), eq(testUserId));
verify(mIpm, times(1)).setApplicationHiddenSettingAsUser(
eq("app3"), eq(false), eq(testUserId));
verify(mIpm, times(0)).setApplicationHiddenSettingAsUser(
eq("app4"), anyBoolean(), eq(testUserId));
}
private void addSystemAppsForIntent(Intent intent, String... packages) throws Exception {
final List<ResolveInfo> resolveInfos = new ArrayList<>();
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
resolveInfos.add(ri);
addInstalledApp(ri, false);
}
when(mPm.queryIntentActivities(argThat(new IntentMatcher(intent)), anyInt()))
.thenReturn(resolveInfos);
}
private void addInapplicableSystemApps(String... packages) throws Exception {
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
addInstalledApp(ri, false);
}
}
private void addDownloadedApps(String... packages) throws Exception {
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfo(pkg);
addInstalledApp(ri, false);
}
}
private void addAppsToTestUser(String... packages) throws Exception {
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfo(pkg);
addInstalledApp(ri, true);
}
}
private void addInstalledApp(ResolveInfo ri, boolean testUser)
throws PackageManager.NameNotFoundException {
final String pkgName = ri.activityInfo.packageName;
final PackageInfo packageInfo = new PackageInfo();
packageInfo.applicationInfo = ri.activityInfo.applicationInfo;
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
if (testUser) {
mTestUserInstalledAppInfos.add(packageInfo.applicationInfo);
} else {
mCurrUserInstalledAppInfos.add(packageInfo.applicationInfo);
}
when(mPm.getPackageInfo(eq(pkgName), anyInt())).thenReturn(packageInfo);
}
private ResolveInfo createResolveInfoForSystemApp(String packageName) {
final ResolveInfo ri = createResolveInfo(packageName);
ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
ri.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
return ri;
}
private ResolveInfo createResolveInfo(String packageName) {
final ResolveInfo ri = new ResolveInfo();
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = packageName;
final ActivityInfo activityInfo = new ActivityInfo();
activityInfo.applicationInfo = applicationInfo;
activityInfo.packageName = packageName;
activityInfo.name = "";
ri.activityInfo = activityInfo;
final ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.applicationInfo = applicationInfo;
serviceInfo.packageName = packageName;
serviceInfo.name = "";
ri.serviceInfo = serviceInfo;
return ri;
}
private static class IntentMatcher implements ArgumentMatcher<Intent> {
private final Intent mIntent;
IntentMatcher(Intent intent) {
mIntent = intent;
}
@Override
public boolean matches(Intent argument) {
return argument != null && argument.filterEquals(mIntent);
}
@Override
public String toString() {
return "Expected: " + mIntent;
}
}
private class TestInjector extends AppCopyHelper.Injector {
TestInjector() {
super(mContext, mTestUser);
}
@Override
UserHandle getUser() {
return mTestUser;
}
@Override
PackageManager getPackageManager() {
return mPm;
}
@Override
IPackageManager getIPackageManager() {
return mIpm;
}
}
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright (C) 2016 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.settingslib.users;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.inputmethod.InputMethodInfo;
import androidx.test.filters.SmallTest;
import com.android.settingslib.BaseTest;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@SmallTest
public class AppRestrictionsHelperTest extends BaseTest {
private @Mock Context mContext;
private @Mock PackageManager mPm;
private @Mock IPackageManager mIpm;
private @Mock UserManager mUm;
private TestInjector mInjector;
private UserHandle mTestUser = UserHandle.of(1111);
private AppRestrictionsHelper mHelper;
private ArrayList<String> mInstalledApps;
private ArrayList<ApplicationInfo> mInstalledAppInfos;
@Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
mInjector = new TestInjector();
final UserInfo user = new UserInfo(
mTestUser.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
when(mUm.getUserInfo(mTestUser.getIdentifier())).thenReturn(user);
mHelper = new AppRestrictionsHelper(mInjector);
mInstalledApps = new ArrayList<>();
mInstalledAppInfos = new ArrayList<>();
}
public void testFetchAndMergeApps() throws Exception {
addSystemAppsWithRequiredAccounts("sys.app0");
addsystemImes(new String[] {"sys.app1", "sys.app2"},
new String[] {"sys.app3", "sys.app4", "sys.app5"});
addSystemAppsForIntent(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
"sys.app1", "sys.app4", "sys.app6");
addSystemAppsForIntent(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE),
"sys.app2", "sys.app5", "sys.app7");
addDownloadedApps("app1", "app2");
when(mPm.getInstalledApplications(anyInt())).thenReturn(mInstalledAppInfos);
mHelper.fetchAndMergeApps();
final ArrayList<String> notExpectedInVisibleApps = new ArrayList<>();
// System apps that require an account and doesn't see restricted account are
// not part of visibleApps.
notExpectedInVisibleApps.add("sys.app0");
// Default system IMEs are not part of visibleApps.
notExpectedInVisibleApps.add("sys.app1");
notExpectedInVisibleApps.add("sys.app2");
final ArrayList<String> expectedInVisibleApps = new ArrayList<>();
expectedInVisibleApps.add("sys.app4");
expectedInVisibleApps.add("sys.app5");
expectedInVisibleApps.add("sys.app6");
expectedInVisibleApps.add("sys.app7");
expectedInVisibleApps.add("app1");
expectedInVisibleApps.add("app2");
for (AppRestrictionsHelper.SelectableAppInfo info : mHelper.getVisibleApps()) {
if (expectedInVisibleApps.contains(info.packageName)) {
expectedInVisibleApps.remove(info.packageName);
} else if (notExpectedInVisibleApps.contains(info.packageName)) {
fail("Package: " + info.packageName + " should not be included in visibleApps");
} else {
fail("Unknown package: " + info.packageName);
}
}
assertEquals("Some expected apps are not inclued in visibleApps: " + expectedInVisibleApps,
0, expectedInVisibleApps.size());
assertFalse("System apps that require an account and doesn't see restricted account "
+ "should be marked for removal", mHelper.isPackageSelected("sys.app0"));
}
public void testApplyUserAppsStates() throws Exception {
final int testUserId = mTestUser.getIdentifier();
mHelper.setPackageSelected("app1", true);
mHelper.setPackageSelected("app2", true);
ApplicationInfo info = new ApplicationInfo();
info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
info.flags |= ApplicationInfo.FLAG_INSTALLED;
when(mIpm.getApplicationInfo(eq("app2"), anyLong(), eq(testUserId)))
.thenReturn(info);
mHelper.setPackageSelected("app3", false);
info = new ApplicationInfo();
when(mIpm.getApplicationInfo(eq("app3"), anyLong(), eq(testUserId)))
.thenReturn(info);
AppRestrictionsHelper.OnDisableUiForPackageListener mockListener =
mock(AppRestrictionsHelper.OnDisableUiForPackageListener.class);
mHelper.applyUserAppsStates(mockListener);
verify(mIpm, times(1)).installExistingPackageAsUser("app1", testUserId,
PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
PackageManager.INSTALL_REASON_UNKNOWN, null);
verify(mIpm, times(1)).setApplicationHiddenSettingAsUser("app2", false, testUserId);
verify(mockListener).onDisableUiForPackage("app2");
verify(mPm, times(1)).deletePackageAsUser(eq("app3"),
nullable(IPackageDeleteObserver.class), anyInt(), eq(mTestUser.getIdentifier()));
}
private void addsystemImes(String[] defaultImes, String[] otherImes) throws
PackageManager.NameNotFoundException, RemoteException {
final ArrayList<InputMethodInfo> inputMethods = new ArrayList<>();
for (String pkg : defaultImes) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final InputMethodInfo inputMethodInfo = new InputMethodInfo(
ri, false, null, null, 0, true, true, false);
inputMethods.add(inputMethodInfo);
addInstalledApp(ri);
}
for (String pkg : otherImes) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final InputMethodInfo inputMethodInfo = new InputMethodInfo(
ri, false, null, null, 0, false, true, false);
inputMethods.add(inputMethodInfo);
addInstalledApp(ri);
}
mInjector.setInputMethodList(inputMethods);
}
private void addSystemAppsForIntent(Intent intent, String... packages) throws Exception {
List<ResolveInfo> resolveInfos = new ArrayList<>();
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
resolveInfos.add(ri);
addInstalledApp(ri);
}
when(mPm.queryIntentActivities(argThat(new IntentMatcher(intent)), anyInt()))
.thenReturn(resolveInfos);
}
private void addSystemAppsWithRequiredAccounts(String... packages) throws Exception {
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final PackageInfo packageInfo = new PackageInfo();
packageInfo.applicationInfo = ri.activityInfo.applicationInfo;
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
packageInfo.requiredAccountType = "account";
packageInfo.restrictedAccountType = null;
mInstalledAppInfos.add(packageInfo.applicationInfo);
when(mPm.getPackageInfo(eq(pkg), anyInt())).thenReturn(packageInfo);
}
}
private void addDownloadedApps(String... packages) throws Exception {
for (String pkg : packages) {
final ResolveInfo ri = createResolveInfo(pkg);
addInstalledApp(ri);
}
}
private void addInstalledApp(ResolveInfo ri) throws PackageManager.NameNotFoundException {
final String pkgName = ri.activityInfo.packageName;
if (mInstalledApps.contains(pkgName)) {
return;
}
final PackageInfo packageInfo = new PackageInfo();
packageInfo.applicationInfo = ri.activityInfo.applicationInfo;
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
mInstalledAppInfos.add(packageInfo.applicationInfo);
when(mPm.getPackageInfo(eq(pkgName), anyInt())).thenReturn(packageInfo);
}
private ResolveInfo createResolveInfoForSystemApp(String packageName) {
final ResolveInfo ri = createResolveInfo(packageName);
ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
ri.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
return ri;
}
private ResolveInfo createResolveInfo(String packageName) {
final ResolveInfo ri = new ResolveInfo();
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = packageName;
final ActivityInfo activityInfo = new ActivityInfo();
activityInfo.applicationInfo = applicationInfo;
activityInfo.packageName = packageName;
activityInfo.name = "";
ri.activityInfo = activityInfo;
final ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.applicationInfo = applicationInfo;
serviceInfo.packageName = packageName;
serviceInfo.name = "";
ri.serviceInfo = serviceInfo;
return ri;
}
private class IntentMatcher implements ArgumentMatcher<Intent> {
private final Intent mIntent;
IntentMatcher(Intent intent) {
mIntent = intent;
}
@Override
public boolean matches(Intent argument) {
return argument != null && argument.filterEquals(mIntent);
}
@Override
public String toString() {
return "Expected: " + mIntent;
}
}
private class TestInjector extends AppRestrictionsHelper.Injector {
List<InputMethodInfo> mImis;
TestInjector() {
super(mContext, mTestUser);
}
@Override
Context getContext() {
return mContext;
}
@Override
UserHandle getUser() {
return mTestUser;
}
@Override
PackageManager getPackageManager() {
return mPm;
}
@Override
IPackageManager getIPackageManager() {
return mIpm;
}
@Override
UserManager getUserManager() {
return mUm;
}
@Override
List<InputMethodInfo> getInputMethodList() {
return mImis;
}
void setInputMethodList(List<InputMethodInfo> imis) {
mImis = imis;
}
}
}

View File

@@ -0,0 +1,283 @@
/*
* 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.settingslib.users;
import static com.android.settingslib.users.AvatarPhotoController.REQUEST_CODE_CHOOSE_PHOTO;
import static com.android.settingslib.users.AvatarPhotoController.REQUEST_CODE_CROP_PHOTO;
import static com.android.settingslib.users.AvatarPhotoController.REQUEST_CODE_TAKE_PHOTO;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
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 java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@RunWith(AndroidJUnit4.class)
public class AvatarPhotoControllerTest {
private static final long TIMEOUT_MILLIS = 5000;
private static final int PHOTO_SIZE = 200;
@Mock AvatarPhotoController.AvatarUi mMockAvatarUi;
private File mImagesDir;
private AvatarPhotoController mController;
private Uri mTakePhotoUri = Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/TakeEditUserPhoto.jpg");
private Uri mCropPhotoUri = Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/CropEditUserPhoto.jpg");
private Context mContext = InstrumentationRegistry.getTargetContext();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockAvatarUi.getPhotoSize()).thenReturn(PHOTO_SIZE);
when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(true);
mImagesDir = new File(
InstrumentationRegistry.getTargetContext().getCacheDir(), "multi_user");
mImagesDir.mkdir();
AvatarPhotoController.ContextInjector contextInjector =
new AvatarPhotoController.ContextInjectorImpl(
InstrumentationRegistry.getTargetContext(), "com.android.settingslib.test");
mController = new AvatarPhotoController(mMockAvatarUi, contextInjector, false);
}
@After
public void tearDown() {
String[] entries = mImagesDir.list();
for (String entry : entries) {
new File(mImagesDir, entry).delete();
}
mImagesDir.delete();
}
@Test
public void takePhotoHasCorrectIntentAndResultCode() {
mController.takePhoto();
verifyStartActivityForResult(
MediaStore.ACTION_IMAGE_CAPTURE_SECURE, REQUEST_CODE_TAKE_PHOTO);
}
@Test
public void choosePhotoHasCorrectIntentAndResultCode() {
mController.choosePhoto();
verifyStartActivityForResult(
MediaStore.ACTION_PICK_IMAGES, REQUEST_CODE_CHOOSE_PHOTO);
}
@Test
public void takePhotoIsFollowedByCrop() throws IOException {
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
intent.setData(Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
verifyStartSystemActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void takePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
intent.setData(Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void takePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
intent.setData(mTakePhotoUri);
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
verifyStartSystemActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsFollowedByCrop() throws IOException {
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
intent.setData(Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
verifyStartSystemActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
intent.setData(Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void choosePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
intent.setData(mTakePhotoUri);
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
verifyStartSystemActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void cropPhotoResultIsReturnedIfResultOkAndContent() {
Intent intent = new Intent();
intent.setData(mCropPhotoUri);
mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_OK, intent);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
}
@Test
public void cropPhotoResultIsNotReturnedIfResultCancel() {
Intent intent = new Intent();
intent.setData(mCropPhotoUri);
mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
}
@Test
public void cropPhotoResultIsNotReturnedIfResultNotContent() {
Intent intent = new Intent();
intent.setData(Uri.parse("file://test"));
mController.onActivityResult(REQUEST_CODE_CROP_PHOTO, Activity.RESULT_OK, intent);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
}
@Test
public void cropDoesNotUseTakePhotoUri() throws IOException {
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
intent.setData(Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
Intent startIntent = verifyStartSystemActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
}
@Test
public void internalCropUsedIfNoSystemCropperFound() throws IOException {
when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
File file = new File(mImagesDir, "file.txt");
saveBitmapToFile(file);
Intent intent = new Intent();
intent.setData(Uri.parse(
"content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
InputStream imageStream = mContext.getContentResolver().openInputStream(mCropPhotoUri);
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
assertThat(bitmap.getWidth()).isEqualTo(PHOTO_SIZE);
assertThat(bitmap.getHeight()).isEqualTo(PHOTO_SIZE);
}
private Intent verifyStartActivityForResult(String action, int resultCode) {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
.startActivityForResult(captor.capture(), eq(resultCode));
Intent intent = captor.getValue();
assertThat(intent.getAction()).isEqualTo(action);
return intent;
}
private Intent verifyStartSystemActivityForResult(String action, int resultCode) {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
.startSystemActivityForResult(captor.capture(), eq(resultCode));
Intent intent = captor.getValue();
assertThat(intent.getAction()).isEqualTo(action);
return intent;
}
private void saveBitmapToFile(File file) throws IOException {
Bitmap bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
OutputStream os = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
os.close();
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2016 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.settingslib.utils;
import static junit.framework.Assert.assertEquals;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.NetworkPolicyEditor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkPolicyEditorTest {
private static final long MAX_LIMIT_BYTES = 500000;
private static final long TEST_LIMIT_BYTES = 2500;
private static final long[] WARNING_BYTES_LIST = {100, 1000, 2000, 3000, 40000};
private NetworkTemplate mNetworkTemplate;
private NetworkPolicyEditor mNetworkPolicyEditor;
@Before
public void setUp() {
mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
.setMeteredness(NetworkStats.METERED_YES)
.setSubscriberIds(Set.of("123456789123456")).build();
NetworkPolicyManager policyManager = NetworkPolicyManager.from(InstrumentationRegistry
.getContext());
mNetworkPolicyEditor = new NetworkPolicyEditor(policyManager);
}
@Test
public void setPolicyWarningBytes_withoutLimit_shouldNotCapWarningBytes() {
// Set the limit to disable so we can change the warning bytes freely
mNetworkPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, NetworkPolicy.LIMIT_DISABLED);
for (int i = 0; i < WARNING_BYTES_LIST.length; i++) {
mNetworkPolicyEditor.setPolicyWarningBytes(mNetworkTemplate, WARNING_BYTES_LIST[i]);
assertEquals(WARNING_BYTES_LIST[i],
mNetworkPolicyEditor.getPolicyWarningBytes(mNetworkTemplate));
}
}
@Test
public void setPolicyWarningBytes_withLimit_shouldCapWarningBytes() {
// Set the limit bytes, so warning bytes cannot exceed the limit bytes.
mNetworkPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, TEST_LIMIT_BYTES);
for (int i = 0; i < WARNING_BYTES_LIST.length; i++) {
mNetworkPolicyEditor.setPolicyWarningBytes(mNetworkTemplate, WARNING_BYTES_LIST[i]);
long expectedWarningBytes = Math.min(WARNING_BYTES_LIST[i], TEST_LIMIT_BYTES);
assertEquals(expectedWarningBytes,
mNetworkPolicyEditor.getPolicyWarningBytes(mNetworkTemplate));
}
}
@Test
public void setPolicyLimitBytes_warningBytesSmallerThanLimit_shouldNotCapWarningBytes() {
long testWarningBytes = MAX_LIMIT_BYTES / 2;
mNetworkPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, MAX_LIMIT_BYTES);
mNetworkPolicyEditor.setPolicyWarningBytes(mNetworkTemplate, testWarningBytes);
assertEquals(MAX_LIMIT_BYTES, mNetworkPolicyEditor.getPolicyLimitBytes(mNetworkTemplate));
assertEquals(testWarningBytes,
mNetworkPolicyEditor.getPolicyWarningBytes(mNetworkTemplate));
}
@Test
public void setPolicyLimitBytes_warningBytesBiggerThanLimit_shouldCapWarningBytes() {
long testWarningBytes = TEST_LIMIT_BYTES * 2;
mNetworkPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, MAX_LIMIT_BYTES);
mNetworkPolicyEditor.setPolicyWarningBytes(mNetworkTemplate, testWarningBytes);
mNetworkPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, TEST_LIMIT_BYTES);
assertEquals(TEST_LIMIT_BYTES, mNetworkPolicyEditor.getPolicyLimitBytes(mNetworkTemplate));
long expectedWarningBytes = Math.min(testWarningBytes, TEST_LIMIT_BYTES);
assertEquals(expectedWarningBytes,
mNetworkPolicyEditor.getPolicyWarningBytes(mNetworkTemplate));
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2016 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.settingslib.utils;
import static junit.framework.Assert.assertTrue;
import android.content.Context;
import android.text.Spanned;
import android.text.style.TtsSpan;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.datetime.ZoneGetter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ZoneGetterTest {
private static final String TIME_ZONE_LONDON_ID = "Europe/London";
private static final String TIME_ZONE_LA_ID = "America/Los_Angeles";
private static final String TIME_ZONE_ALGIERS_ID = "Africa/Algiers";
private static final String TIME_ZONE_CEUTA_ID = "Africa/Ceuta";
private Locale mLocaleEnUs;
private Calendar mCalendar;
@Before
public void setUp() {
mLocaleEnUs = new Locale("en", "us");
Locale.setDefault(mLocaleEnUs);
mCalendar = new GregorianCalendar(2016, 9, 1);
}
@Test
public void getTimeZoneOffsetAndName_setLondon_returnBritishSummerTime() {
// Check it will ends with 'British Summer Time', not 'London' or sth else
testTimeZoneOffsetAndNameInner(TIME_ZONE_LONDON_ID, "British Summer Time");
}
@Test
public void getTimeZoneOffsetAndName_setLosAngeles_returnPacificDaylightTime() {
// Check it will ends with 'Pacific Daylight Time', not 'Los_Angeles'
testTimeZoneOffsetAndNameInner(TIME_ZONE_LA_ID, "Pacific Daylight Time");
}
@Test
public void getTimeZoneOffsetAndName_setAlgiers_returnCentralEuropeanStandardTime() {
testTimeZoneOffsetAndNameInner(TIME_ZONE_ALGIERS_ID, "Central European Standard Time");
}
@Test
public void getTimeZoneOffsetAndName_setCeuta_returnCentralEuropeanSummerTime() {
testTimeZoneOffsetAndNameInner(TIME_ZONE_CEUTA_ID, "Central European Summer Time");
}
@Test
public void getZonesList_checkTypes() {
final List<Map<String, Object>> zones =
ZoneGetter.getZonesList(InstrumentationRegistry.getContext());
for (Map<String, Object> zone : zones) {
assertTrue(zone.get(ZoneGetter.KEY_DISPLAYNAME) instanceof String);
assertTrue(zone.get(ZoneGetter.KEY_DISPLAY_LABEL) instanceof CharSequence);
assertTrue(zone.get(ZoneGetter.KEY_OFFSET) instanceof Integer);
assertTrue(zone.get(ZoneGetter.KEY_OFFSET_LABEL) instanceof CharSequence);
assertTrue(zone.get(ZoneGetter.KEY_ID) instanceof String);
assertTrue(zone.get(ZoneGetter.KEY_GMT) instanceof String);
}
}
@Test
public void getTimeZoneOffsetAndName_withTtsSpan() {
final Context context = InstrumentationRegistry.getContext();
final TimeZone timeZone = TimeZone.getTimeZone(TIME_ZONE_LA_ID);
CharSequence timeZoneString = ZoneGetter.getTimeZoneOffsetAndName(context, timeZone,
mCalendar.getTime());
assertTrue("Time zone string should be spanned", timeZoneString instanceof Spanned);
assertTrue("Time zone display name should have TTS spans",
((Spanned) timeZoneString).getSpans(
0, timeZoneString.length(), TtsSpan.class).length > 0);
}
private void testTimeZoneOffsetAndNameInner(String timeZoneId, String expectedName) {
final Context context = InstrumentationRegistry.getContext();
final TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
CharSequence timeZoneString = ZoneGetter.getTimeZoneOffsetAndName(context, timeZone,
mCalendar.getTime());
assertTrue(timeZoneString.toString().endsWith(expectedName));
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2024 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.settingslib.view.accessibility.data.repository
import android.view.accessibility.CaptioningManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
class CaptioningRepositoryTest {
@Captor
private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener>
@Mock private lateinit var captioningManager: CaptioningManager
private lateinit var underTest: CaptioningRepository
private val testScope = TestScope()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest =
CaptioningRepositoryImpl(
captioningManager,
testScope.testScheduler,
testScope.backgroundScope
)
}
@Test
fun isSystemAudioCaptioningEnabled_change_repositoryEmits() {
testScope.runTest {
`when`(captioningManager.isEnabled).thenReturn(false)
val isSystemAudioCaptioningEnabled = mutableListOf<Boolean>()
underTest.isSystemAudioCaptioningEnabled
.onEach { isSystemAudioCaptioningEnabled.add(it) }
.launchIn(backgroundScope)
runCurrent()
triggerOnSystemAudioCaptioningChange()
runCurrent()
assertThat(isSystemAudioCaptioningEnabled)
.containsExactlyElementsIn(listOf(false, true))
.inOrder()
}
}
@Test
fun isSystemAudioCaptioningUiEnabled_change_repositoryEmits() {
testScope.runTest {
`when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(false)
val isSystemAudioCaptioningUiEnabled = mutableListOf<Boolean>()
underTest.isSystemAudioCaptioningUiEnabled
.onEach { isSystemAudioCaptioningUiEnabled.add(it) }
.launchIn(backgroundScope)
runCurrent()
triggerSystemAudioCaptioningUiChange()
runCurrent()
assertThat(isSystemAudioCaptioningUiEnabled)
.containsExactlyElementsIn(listOf(false, true))
.inOrder()
}
}
private fun triggerSystemAudioCaptioningUiChange(enabled: Boolean = true) {
verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture())
listenerCaptor.value.onSystemAudioCaptioningUiChanged(enabled)
}
private fun triggerOnSystemAudioCaptioningChange(enabled: Boolean = true) {
verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture())
listenerCaptor.value.onSystemAudioCaptioningChanged(enabled)
}
}

View File

@@ -0,0 +1 @@
include /packages/SettingsLib/src/com/android/settingslib/volume/OWNERS

View File

@@ -0,0 +1,262 @@
/*
* Copyright (C) 2024 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.settingslib.volume.data.repository
import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class AudioRepositoryTest {
@Captor
private lateinit var modeListenerCaptor: ArgumentCaptor<AudioManager.OnModeChangedListener>
@Captor
private lateinit var communicationDeviceListenerCaptor:
ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener>
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var communicationDevice: AudioDeviceInfo
private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
private val isAffectedByRingerModeByStream: MutableMap<Int, Boolean> = mutableMapOf()
private val isMuteByStream: MutableMap<Int, Boolean> = mutableMapOf()
private val testScope = TestScope()
private lateinit var underTest: AudioRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(audioManager.mode).thenReturn(AudioManager.MODE_RINGTONE)
`when`(audioManager.communicationDevice).thenReturn(communicationDevice)
`when`(audioManager.getStreamMinVolume(anyInt())).thenReturn(MIN_VOLUME)
`when`(audioManager.getStreamMaxVolume(anyInt())).thenReturn(MAX_VOLUME)
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
`when`(audioManager.setStreamVolume(anyInt(), anyInt(), anyInt())).then {
val streamType = it.arguments[1] as Int
volumeByStream[it.arguments[0] as Int] = streamType
triggerEvent(AudioManagerEvent.StreamVolumeChanged(AudioStream(streamType)))
}
`when`(audioManager.adjustStreamVolume(anyInt(), anyInt(), anyInt())).then {
val streamType = it.arguments[0] as Int
isMuteByStream[streamType] = it.arguments[2] == AudioManager.ADJUST_MUTE
triggerEvent(AudioManagerEvent.StreamMuteChanged(AudioStream(streamType)))
}
`when`(audioManager.getStreamVolume(anyInt())).thenAnswer {
volumeByStream.getOrDefault(it.arguments[0] as Int, 0)
}
`when`(audioManager.isStreamAffectedByRingerMode(anyInt())).thenAnswer {
isAffectedByRingerModeByStream.getOrDefault(it.arguments[0] as Int, false)
}
`when`(audioManager.isStreamMute(anyInt())).thenAnswer {
isMuteByStream.getOrDefault(it.arguments[0] as Int, false)
}
underTest =
AudioRepositoryImpl(
eventsReceiver,
audioManager,
testScope.testScheduler,
testScope.backgroundScope,
)
}
@Test
fun audioModeChanges_repositoryEmits() {
testScope.runTest {
val modes = mutableListOf<Int>()
underTest.mode.onEach { modes.add(it) }.launchIn(backgroundScope)
runCurrent()
triggerModeChange(AudioManager.MODE_IN_CALL)
runCurrent()
assertThat(modes).containsExactly(AudioManager.MODE_RINGTONE, AudioManager.MODE_IN_CALL)
}
}
@Test
fun ringerModeChanges_repositoryEmits() {
testScope.runTest {
val modes = mutableListOf<RingerMode>()
underTest.ringerMode.onEach { modes.add(it) }.launchIn(backgroundScope)
runCurrent()
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
triggerEvent(AudioManagerEvent.InternalRingerModeChanged)
runCurrent()
assertThat(modes)
.containsExactly(
RingerMode(AudioManager.RINGER_MODE_NORMAL),
RingerMode(AudioManager.RINGER_MODE_SILENT),
)
}
}
@Test
fun communicationDeviceChanges_repositoryEmits() {
testScope.runTest {
var device: AudioDeviceInfo? = null
underTest.communicationDevice.onEach { device = it }.launchIn(backgroundScope)
runCurrent()
triggerConnectedDeviceChange(communicationDevice)
runCurrent()
assertThat(device).isSameInstanceAs(communicationDevice)
}
}
@Test
fun adjustingVolume_changesTheStream() {
testScope.runTest {
val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
var streamModel: AudioStreamModel? = null
underTest
.getAudioStream(audioStream)
.onEach { streamModel = it }
.launchIn(backgroundScope)
runCurrent()
underTest.setVolume(audioStream, 50)
runCurrent()
assertThat(streamModel)
.isEqualTo(
AudioStreamModel(
audioStream = audioStream,
volume = 50,
minVolume = MIN_VOLUME,
maxVolume = MAX_VOLUME,
isAffectedByRingerMode = false,
isMuted = false,
)
)
}
}
@Test
fun muteStream_mutesTheStream() {
testScope.runTest {
val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
var streamModel: AudioStreamModel? = null
underTest
.getAudioStream(audioStream)
.onEach { streamModel = it }
.launchIn(backgroundScope)
runCurrent()
underTest.setMuted(audioStream, true)
runCurrent()
assertThat(streamModel)
.isEqualTo(
AudioStreamModel(
audioStream = audioStream,
volume = 0,
minVolume = MIN_VOLUME,
maxVolume = MAX_VOLUME,
isAffectedByRingerMode = false,
isMuted = true,
)
)
}
}
@Test
fun unmuteStream_unmutesTheStream() {
testScope.runTest {
val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
isMuteByStream[audioStream.value] = true
var streamModel: AudioStreamModel? = null
underTest
.getAudioStream(audioStream)
.onEach { streamModel = it }
.launchIn(backgroundScope)
runCurrent()
underTest.setMuted(audioStream, false)
runCurrent()
assertThat(streamModel)
.isEqualTo(
AudioStreamModel(
audioStream = audioStream,
volume = 0,
minVolume = MIN_VOLUME,
maxVolume = MAX_VOLUME,
isAffectedByRingerMode = false,
isMuted = false,
)
)
}
}
private fun triggerConnectedDeviceChange(communicationDevice: AudioDeviceInfo?) {
verify(audioManager)
.addOnCommunicationDeviceChangedListener(
any(),
communicationDeviceListenerCaptor.capture(),
)
communicationDeviceListenerCaptor.value.onCommunicationDeviceChanged(communicationDevice)
}
private fun triggerModeChange(mode: Int) {
verify(audioManager).addOnModeChangedListener(any(), modeListenerCaptor.capture())
modeListenerCaptor.value.onModeChanged(mode)
}
private fun triggerEvent(event: AudioManagerEvent) {
testScope.launch { eventsReceiver.triggerEvent(event) }
}
private companion object {
const val MIN_VOLUME = 0
const val MAX_VOLUME = 100
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2024 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.settingslib.volume.data.repository
import android.media.MediaRoute2Info
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class LocalMediaRepositoryImplTest {
@Mock private lateinit var localMediaManager: LocalMediaManager
@Mock private lateinit var mediaDevice1: MediaDevice
@Mock private lateinit var mediaDevice2: MediaDevice
@Mock private lateinit var mediaRouter2Manager: MediaRouter2Manager
@Captor
private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val testScope = TestScope()
private lateinit var underTest: LocalMediaRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest =
LocalMediaRepositoryImpl(
eventsReceiver,
localMediaManager,
mediaRouter2Manager,
testScope.backgroundScope,
testScope.testScheduler,
)
}
@Test
fun mediaDevices_areUpdated() {
testScope.runTest {
var mediaDevices: Collection<MediaDevice>? = null
underTest.mediaDevices.onEach { mediaDevices = it }.launchIn(backgroundScope)
runCurrent()
verify(localMediaManager).registerCallback(deviceCallbackCaptor.capture())
deviceCallbackCaptor.value.onDeviceListUpdate(listOf(mediaDevice1, mediaDevice2))
runCurrent()
assertThat(mediaDevices).hasSize(2)
assertThat(mediaDevices).contains(mediaDevice1)
assertThat(mediaDevices).contains(mediaDevice2)
}
}
@Test
fun deviceListUpdated_currentConnectedDeviceUpdated() {
testScope.runTest {
var currentConnectedDevice: MediaDevice? = null
underTest.currentConnectedDevice
.onEach { currentConnectedDevice = it }
.launchIn(backgroundScope)
runCurrent()
`when`(localMediaManager.currentConnectedDevice).thenReturn(mediaDevice1)
verify(localMediaManager).registerCallback(deviceCallbackCaptor.capture())
deviceCallbackCaptor.value.onDeviceListUpdate(listOf(mediaDevice1, mediaDevice2))
runCurrent()
assertThat(currentConnectedDevice).isEqualTo(mediaDevice1)
}
}
@Test
fun kek() {
testScope.runTest {
`when`(localMediaManager.remoteRoutingSessions)
.thenReturn(
listOf(
testRoutingSessionInfo1,
testRoutingSessionInfo2,
testRoutingSessionInfo3,
)
)
`when`(localMediaManager.shouldEnableVolumeSeekBar(any())).then {
(it.arguments[0] as RoutingSessionInfo) == testRoutingSessionInfo1
}
`when`(mediaRouter2Manager.getTransferableRoutes(any<RoutingSessionInfo>())).then {
if ((it.arguments[0] as RoutingSessionInfo) == testRoutingSessionInfo2) {
return@then listOf(mock(MediaRoute2Info::class.java))
}
emptyList<MediaRoute2Info>()
}
var remoteRoutingSessions: Collection<RoutingSession>? = null
underTest.remoteRoutingSessions
.onEach { remoteRoutingSessions = it }
.launchIn(backgroundScope)
runCurrent()
assertThat(remoteRoutingSessions)
.containsExactlyElementsIn(
listOf(
RoutingSession(
routingSessionInfo = testRoutingSessionInfo1,
isVolumeSeekBarEnabled = true,
isMediaOutputDisabled = true,
),
RoutingSession(
routingSessionInfo = testRoutingSessionInfo2,
isVolumeSeekBarEnabled = false,
isMediaOutputDisabled = false,
),
RoutingSession(
routingSessionInfo = testRoutingSessionInfo3,
isVolumeSeekBarEnabled = false,
isMediaOutputDisabled = true,
)
)
)
}
}
@Test
fun adjustSessionVolume_adjusts() {
testScope.runTest {
var volume = 0
`when`(localMediaManager.adjustSessionVolume(anyString(), anyInt())).then {
volume = it.arguments[1] as Int
Unit
}
underTest.adjustSessionVolume("test_session", 10)
assertThat(volume).isEqualTo(10)
}
}
private companion object {
val testRoutingSessionInfo1 =
RoutingSessionInfo.Builder("id_1", "test.pkg.1").addSelectedRoute("route_1").build()
val testRoutingSessionInfo2 =
RoutingSessionInfo.Builder("id_2", "test.pkg.2").addSelectedRoute("route_2").build()
val testRoutingSessionInfo3 =
RoutingSessionInfo.Builder("id_3", "test.pkg.3").addSelectedRoute("route_3").build()
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2024 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.settingslib.volume.data.repository
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class MediaControllerRepositoryImplTest {
@Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothCallback>
@Mock private lateinit var mediaSessionManager: MediaSessionManager
@Mock private lateinit var localBluetoothManager: LocalBluetoothManager
@Mock private lateinit var eventManager: BluetoothEventManager
@Mock private lateinit var stoppedMediaController: MediaController
@Mock private lateinit var statelessMediaController: MediaController
@Mock private lateinit var errorMediaController: MediaController
@Mock private lateinit var remoteMediaController: MediaController
@Mock private lateinit var localMediaController: MediaController
@Mock private lateinit var remotePlaybackInfo: PlaybackInfo
@Mock private lateinit var localPlaybackInfo: PlaybackInfo
private val testScope = TestScope()
private val eventsReceiver = FakeAudioManagerEventsReceiver()
private lateinit var underTest: MediaControllerRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(localBluetoothManager.eventManager).thenReturn(eventManager)
`when`(stoppedMediaController.playbackState).thenReturn(stateStopped)
`when`(stoppedMediaController.packageName).thenReturn("test.pkg.stopped")
`when`(statelessMediaController.playbackState).thenReturn(stateNone)
`when`(statelessMediaController.packageName).thenReturn("test.pkg.stateless")
`when`(errorMediaController.playbackState).thenReturn(stateError)
`when`(errorMediaController.packageName).thenReturn("test.pkg.error")
`when`(remoteMediaController.playbackState).thenReturn(statePlaying)
`when`(remoteMediaController.playbackInfo).thenReturn(remotePlaybackInfo)
`when`(remoteMediaController.packageName).thenReturn("test.pkg.remote")
`when`(localMediaController.playbackState).thenReturn(statePlaying)
`when`(localMediaController.playbackInfo).thenReturn(localPlaybackInfo)
`when`(localMediaController.packageName).thenReturn("test.pkg.local")
`when`(remotePlaybackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
`when`(localPlaybackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
underTest =
MediaControllerRepositoryImpl(
eventsReceiver,
mediaSessionManager,
localBluetoothManager,
testScope.backgroundScope,
testScope.testScheduler,
)
}
@Test
fun playingMediaDevicesAvailable_sessionIsActive() {
testScope.runTest {
`when`(mediaSessionManager.getActiveSessions(any()))
.thenReturn(
listOf(
stoppedMediaController,
statelessMediaController,
errorMediaController,
remoteMediaController,
localMediaController
)
)
var mediaController: MediaController? = null
underTest.activeLocalMediaController
.onEach { mediaController = it }
.launchIn(backgroundScope)
runCurrent()
eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
assertThat(mediaController).isSameInstanceAs(localMediaController)
}
}
@Test
fun noPlayingMediaDevicesAvailable_sessionIsInactive() {
testScope.runTest {
`when`(mediaSessionManager.getActiveSessions(any()))
.thenReturn(
listOf(
stoppedMediaController,
statelessMediaController,
errorMediaController,
)
)
var mediaController: MediaController? = null
underTest.activeLocalMediaController
.onEach { mediaController = it }
.launchIn(backgroundScope)
runCurrent()
eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
assertThat(mediaController).isNull()
}
}
private fun triggerOnAudioModeChanged() {
verify(eventManager).registerCallback(callbackCaptor.capture())
callbackCaptor.value.onAudioModeChanged()
}
private companion object {
val statePlaying: PlaybackState =
PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0, 0f).build()
val stateError: PlaybackState =
PlaybackState.Builder().setState(PlaybackState.STATE_ERROR, 0, 0f).build()
val stateStopped: PlaybackState =
PlaybackState.Builder().setState(PlaybackState.STATE_STOPPED, 0, 0f).build()
val stateNone: PlaybackState =
PlaybackState.Builder().setState(PlaybackState.STATE_NONE, 0, 0f).build()
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2024 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.settingslib.volume.shared
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.google.common.truth.Expect
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
class AudioManagerEventsReceiverTest {
@JvmField @Rule val expect = Expect.create()
private val testScope = TestScope()
@Mock private lateinit var context: Context
@Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
private lateinit var underTest: AudioManagerEventsReceiver
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest = AudioManagerEventsReceiverImpl(context, testScope.backgroundScope)
}
@Test
fun validIntent_translatedToEvent() {
testScope.runTest {
val events = mutableListOf<AudioManagerEvent>()
underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
runCurrent()
triggerIntent(
Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION).apply {
putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
}
)
triggerIntent(
Intent(AudioManager.VOLUME_CHANGED_ACTION).apply {
putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
}
)
triggerIntent(Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION))
triggerIntent(Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION))
triggerIntent(Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION))
runCurrent()
expect
.that(events)
.containsExactly(
AudioManagerEvent.StreamMuteChanged(
AudioStream(AudioManager.STREAM_SYSTEM),
),
AudioManagerEvent.StreamVolumeChanged(
AudioStream(AudioManager.STREAM_SYSTEM),
),
AudioManagerEvent.StreamMasterMuteChanged,
AudioManagerEvent.InternalRingerModeChanged,
AudioManagerEvent.StreamDevicesChanged,
)
}
}
@Test
fun streamAudioManagerEvent_withoutAudioStream_areSkipped() {
testScope.runTest {
val events = mutableListOf<AudioManagerEvent>()
underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
runCurrent()
triggerIntent(Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION))
triggerIntent(Intent(AudioManager.VOLUME_CHANGED_ACTION))
runCurrent()
expect.that(events).isEmpty()
}
}
@Test
fun invalidIntents_areSkipped() {
testScope.runTest {
val events = mutableListOf<AudioManagerEvent>()
underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
runCurrent()
triggerIntent(null)
triggerIntent(Intent())
triggerIntent(Intent("invalid_action"))
runCurrent()
expect.that(events).isEmpty()
}
}
private fun triggerIntent(intent: Intent?) {
verify(context).registerReceiver(receiverCaptor.capture(), any())
receiverCaptor.value.onReceive(context, intent)
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 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.settingslib.volume.shared
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
class FakeAudioManagerEventsReceiver : AudioManagerEventsReceiver {
private val mutableIntents = MutableSharedFlow<AudioManagerEvent>()
override val events: SharedFlow<AudioManagerEvent> = mutableIntents.asSharedFlow()
suspend fun triggerEvent(event: AudioManagerEvent) {
mutableIntents.emit(event)
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.widget.spinner.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class SettingsSpinnerPreferenceTest {
private Context mContext;
private PreferenceViewHolder mViewHolder;
private Spinner mSpinner;
private SettingsSpinnerPreference mSpinnerPreference;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mSpinnerPreference = new SettingsSpinnerPreference(mContext);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final View rootView = inflater.inflate(mSpinnerPreference.getLayoutResource(),
new LinearLayout(mContext), false /* attachToRoot */);
mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView);
mSpinner = (Spinner) mViewHolder.findViewById(R.id.spinner);
}
@Test
public void onBindViewHolder_noSetSelection_getDefaultItem() {
final List<CharSequence> list = new ArrayList<>();
list.add("TEST1");
list.add("TEST2");
list.add("TEST3");
final SettingsSpinnerAdapter adapter = new SettingsSpinnerAdapter(mContext);
adapter.addAll(list);
mSpinnerPreference.setAdapter(adapter);
mSpinnerPreference.onBindViewHolder(mViewHolder);
assertThat(adapter).isEqualTo(mSpinner.getAdapter());
assertThat(mSpinnerPreference.getSelectedItem())
.isEqualTo(mSpinner.getAdapter().getItem(0));
}
@Test
public void onBindViewHolder_setSelection_getSelectedItem() {
final List<CharSequence> list = new ArrayList<>();
list.add("TEST1");
list.add("TEST2");
list.add("TEST3");
final SettingsSpinnerAdapter adapter = new SettingsSpinnerAdapter(mContext);
adapter.addAll(list);
mSpinnerPreference.setAdapter(adapter);
mSpinnerPreference.setSelection(1);
mSpinnerPreference.onBindViewHolder(mViewHolder);
assertThat(mSpinnerPreference.getSelectedItem())
.isEqualTo(mSpinner.getAdapter().getItem(1));
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import android.content.Context;
import android.text.SpannedString;
import android.text.style.AbsoluteSizeSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
import com.android.settingslib.widget.preference.usage.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class UsageProgressBarPreferenceTest {
private UsageProgressBarPreference mUsageProgressBarPreference;
private PreferenceViewHolder mViewHolder;
@Before
public void setUp() {
final Context context = ApplicationProvider.getApplicationContext();
mUsageProgressBarPreference = new UsageProgressBarPreference(context);
final LayoutInflater inflater = LayoutInflater.from(context);
final View rootView = inflater.inflate(mUsageProgressBarPreference.getLayoutResource(),
new LinearLayout(context), false /* attachToRoot */);
mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView);
}
@Test
public void setUsageSummary_noNumber_noAbsoluteSizeSpan() {
mUsageProgressBarPreference.setUsageSummary("test");
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
final SpannedString summary = new SpannedString(usageSummary.getText());
assertThat(summary.getSpans(0, summary.length(), AbsoluteSizeSpan.class).length)
.isEqualTo(0);
}
@Test
public void setUsageSummary_integerNumber_findAbsoluteSizeSpan() {
mUsageProgressBarPreference.setUsageSummary("10Test");
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
final SpannedString summary = new SpannedString(usageSummary.getText());
final AbsoluteSizeSpan[] spans = summary
.getSpans(0, summary.length(), AbsoluteSizeSpan.class);
assertThat(spans.length).isEqualTo(1);
assertThat(summary.getSpanStart(spans[0])).isEqualTo(0);
assertThat(summary.getSpanEnd(spans[0])).isEqualTo(2);
}
@Test
public void setUsageSummary_floatingPointNumber_findAbsoluteSizeSpan() {
mUsageProgressBarPreference.setUsageSummary("3.14Test");
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
final SpannedString summary = new SpannedString(usageSummary.getText());
final AbsoluteSizeSpan[] spans = summary
.getSpans(0, summary.length(), AbsoluteSizeSpan.class);
assertThat(spans.length).isEqualTo(1);
assertThat(summary.getSpanStart(spans[0])).isEqualTo(0);
assertThat(summary.getSpanEnd(spans[0])).isEqualTo(4);
}
@Test
public void setUsageSummary_commaFloatingPointNumber_findAbsoluteSizeSpan() {
mUsageProgressBarPreference.setUsageSummary("3,14Test");
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
final SpannedString summary = new SpannedString(usageSummary.getText());
final AbsoluteSizeSpan[] spans = summary
.getSpans(0, summary.length(), AbsoluteSizeSpan.class);
assertThat(spans.length).isEqualTo(1);
assertThat(summary.getSpanStart(spans[0])).isEqualTo(0);
assertThat(summary.getSpanEnd(spans[0])).isEqualTo(4);
}
@Test
public void setBottomSummary_getCorrectSummary() {
final String expectedText = "Should last until about 7:45 PM";
mUsageProgressBarPreference.setBottomSummary(expectedText);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
assertThat(bottomSummary.getText()).isEqualTo(expectedText);
assertThat(bottomSummary.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void setBottomSummary_emptyText_isGone() {
mUsageProgressBarPreference.setBottomSummary(null);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
assertThat(bottomSummary.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void setPercent_getCorrectProgress() {
mUsageProgressBarPreference.setPercent(31, 80);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final ProgressBar progressBar = (ProgressBar) mViewHolder
.findViewById(android.R.id.progress);
assertThat(progressBar.getProgress()).isEqualTo((int) (31.0f / 80 * 100));
}
@Test
public void setPercent_totalSizeZero_getProgressZero() {
mUsageProgressBarPreference.setPercent(0 /* usage */, 0 /* total */);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final ProgressBar progressBar = (ProgressBar) mViewHolder
.findViewById(android.R.id.progress);
assertThat(progressBar.getProgress()).isEqualTo(0);
}
@Test
public void setCustomContent_setNullImageView_noChild() {
mUsageProgressBarPreference.setCustomContent(null /* imageView */);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final FrameLayout customContent =
(FrameLayout) mViewHolder.findViewById(R.id.custom_content);
assertThat(customContent.getChildCount()).isEqualTo(0);
assertThat(customContent.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void setCustomContent_setImageView_oneChild() {
final ImageView imageView = mock(ImageView.class);
mUsageProgressBarPreference.setCustomContent(imageView);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final FrameLayout customContent =
(FrameLayout) mViewHolder.findViewById(R.id.custom_content);
assertThat(customContent.getChildCount()).isEqualTo(1);
assertThat(customContent.getChildAt(0)).isEqualTo(imageView);
assertThat(customContent.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void setCustomContent_setImageViewTwice_oneAndLatestChild() {
final ImageView imageViewLegacy = mock(ImageView.class);
final ImageView imageViewNew = mock(ImageView.class);
mUsageProgressBarPreference.setCustomContent(imageViewLegacy);
mUsageProgressBarPreference.setCustomContent(imageViewNew);
mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
final FrameLayout customContent =
(FrameLayout) mViewHolder.findViewById(R.id.custom_content);
assertThat(customContent.getChildCount()).isEqualTo(1);
assertThat(customContent.getChildAt(0)).isEqualTo(imageViewNew);
assertThat(customContent.getVisibility()).isEqualTo(View.VISIBLE);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
# People who can approve changes for submission
arcwang@google.com
govenliu@google.com
qal@google.com

File diff suppressed because it is too large Load Diff