1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.spaprivileged.model.app 18 19 import android.app.Application 20 import android.content.pm.ApplicationInfo 21 import androidx.compose.runtime.Composable 22 import androidx.test.ext.junit.runners.AndroidJUnit4 23 import com.android.settingslib.spa.framework.compose.stateOf 24 import com.android.settingslib.spa.framework.util.mapItem 25 import com.android.settingslib.spa.testutils.waitUntil 26 import com.android.settingslib.spaprivileged.template.app.AppListConfig 27 import com.google.common.truth.Truth.assertThat 28 import kotlinx.coroutines.ExperimentalCoroutinesApi 29 import kotlinx.coroutines.flow.Flow 30 import kotlinx.coroutines.flow.first 31 import kotlinx.coroutines.flow.flowOf 32 import kotlinx.coroutines.test.runTest 33 import org.junit.Rule 34 import org.junit.Test 35 import org.junit.runner.RunWith 36 import org.mockito.Mock 37 import org.mockito.junit.MockitoJUnit 38 import org.mockito.junit.MockitoRule 39 40 @OptIn(ExperimentalCoroutinesApi::class) 41 @RunWith(AndroidJUnit4::class) 42 class AppListViewModelTest { 43 @get:Rule 44 val mockito: MockitoRule = MockitoJUnit.rule() 45 46 @Mock 47 private lateinit var application: Application 48 49 private val listModel = TestAppListModel() 50 51 private fun createViewModel(): AppListViewModelImpl<TestAppRecord> { 52 val viewModel = AppListViewModelImpl<TestAppRecord>( 53 application = application, 54 appListRepositoryFactory = { FakeAppListRepository }, 55 appRepositoryFactory = { FakeAppRepository }, 56 ) 57 viewModel.appListConfig.setIfAbsent(CONFIG) 58 viewModel.listModel.setIfAbsent(listModel) 59 viewModel.showSystem.setIfAbsent(false) 60 viewModel.optionFlow.value = 0 61 viewModel.searchQuery.setIfAbsent("") 62 viewModel.reloadApps() 63 return viewModel 64 } 65 66 @Test 67 fun appListDataFlow() = runTest { 68 val viewModel = createViewModel() 69 70 val (appEntries, option) = viewModel.appListDataFlow.first() 71 72 assertThat(appEntries).hasSize(1) 73 assertThat(appEntries[0].record.app).isSameInstanceAs(APP) 74 assertThat(appEntries[0].label).isEqualTo(LABEL) 75 assertThat(option).isEqualTo(0) 76 } 77 78 @Test 79 fun onFirstLoaded_calledWhenLoaded() = runTest { 80 val viewModel = createViewModel() 81 82 viewModel.appListDataFlow.first() 83 84 waitUntil { listModel.onFirstLoadedCalled } 85 } 86 87 private object FakeAppListRepository : AppListRepository { 88 override suspend fun loadApps( 89 userId: Int, 90 loadInstantApps: Boolean, 91 matchAnyUserForAdmin: Boolean, 92 ) = listOf(APP) 93 94 override fun showSystemPredicate( 95 userIdFlow: Flow<Int>, 96 showSystemFlow: Flow<Boolean>, 97 ): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { true } 98 99 override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet() 100 101 override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) = 102 emptyList<ApplicationInfo>() 103 } 104 105 private object FakeAppRepository : AppRepository { 106 override fun loadLabel(app: ApplicationInfo) = LABEL 107 108 @Composable 109 override fun produceIcon(app: ApplicationInfo) = stateOf(null) 110 111 @Composable 112 override fun produceIconContentDescription(app: ApplicationInfo) = stateOf(null) 113 } 114 115 private companion object { 116 const val USER_ID = 0 117 const val PACKAGE_NAME = "package.name" 118 const val LABEL = "Label" 119 val CONFIG = AppListConfig( 120 userIds = listOf(USER_ID), 121 showInstantApps = false, 122 matchAnyUserForAdmin = false, 123 ) 124 val APP = ApplicationInfo().apply { 125 packageName = PACKAGE_NAME 126 } 127 } 128 } 129 130 private data class TestAppRecord(override val app: ApplicationInfo) : AppRecord 131 132 private class TestAppListModel : AppListModel<TestAppRecord> { 133 var onFirstLoadedCalled = false 134 135 override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = 136 appListFlow.mapItem(::TestAppRecord) 137 138 override suspend fun onFirstLoaded(recordList: List<TestAppRecord>): Boolean { 139 onFirstLoadedCalled = true 140 return false 141 } 142 } 143