summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2024-01-23 12:21:43 -0500
committer Mark Renouf <mrenouf@google.com> 2024-01-25 19:56:19 -0500
commitf30cb97a784ba508a82863ef74ea0135355aad0c (patch)
treefee5089bc3b3bf2b33e5caa9851303e7b0c52b07 /tests
parentc5d725eec15d2cff2aab08437948d6d0f2d01a63 (diff)
UserInteractor and Profile
A domain component which applies business logic to User data. This component maps Users to Profiles, the set of which is defined by Profile.Type Bug: 309960444 Test: atest IntentResolver-tests-unit:UserInteractorTest \ FakeUserRepositoryTest Change-Id: I9832836ae019ba1b0ae45366f9fc0e26bc9b23ce
Diffstat (limited to 'tests')
-rw-r--r--tests/shared/src/com/android/intentresolver/v2/data/repository/FakeUserRepository.kt61
-rw-r--r--tests/unit/src/com/android/intentresolver/v2/data/repository/FakeUserRepositoryTest.kt108
-rw-r--r--tests/unit/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt26
-rw-r--r--tests/unit/src/com/android/intentresolver/v2/domain/interactor/UserInteractorTest.kt179
4 files changed, 362 insertions, 12 deletions
diff --git a/tests/shared/src/com/android/intentresolver/v2/data/repository/FakeUserRepository.kt b/tests/shared/src/com/android/intentresolver/v2/data/repository/FakeUserRepository.kt
new file mode 100644
index 00000000..5ed6f506
--- /dev/null
+++ b/tests/shared/src/com/android/intentresolver/v2/data/repository/FakeUserRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.intentresolver.v2.data.repository
+
+import com.android.intentresolver.v2.shared.model.User
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.update
+
+/** A simple repository which can be initialized from a list and updated. */
+class FakeUserRepository(vararg userList: User) : UserRepository {
+ internal data class UserState(val user: User, val available: Boolean)
+
+ private val userState = MutableStateFlow(userList.map { UserState(it, available = true) })
+
+ // Expose a List<User> from List<UserState>
+ override val users = userState.map { userList -> userList.map { it.user } }
+
+ fun addUser(user: User, available: Boolean) {
+ require(userState.value.none { it.user.id == user.id }) {
+ "A User with ${user.id} already exists!"
+ }
+ userState.update { it + UserState(user, available) }
+ }
+
+ fun removeUser(user: User) {
+ require(userState.value.any { it.user.id == user.id }) {
+ "A User with ${user.id} does not exist!"
+ }
+ userState.update { it.filterNot { state -> state.user.id == user.id } }
+ }
+
+ override val availability =
+ userState.map { userStateList -> userStateList.associate { it.user to it.available } }
+
+ override suspend fun requestState(user: User, available: Boolean) {
+ userState.update { userStateList ->
+ userStateList.map { userState ->
+ if (userState.user.id == user.id) {
+ UserState(user, available)
+ } else {
+ userState
+ }
+ }
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/intentresolver/v2/data/repository/FakeUserRepositoryTest.kt b/tests/unit/src/com/android/intentresolver/v2/data/repository/FakeUserRepositoryTest.kt
new file mode 100644
index 00000000..334f31ad
--- /dev/null
+++ b/tests/unit/src/com/android/intentresolver/v2/data/repository/FakeUserRepositoryTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.intentresolver.v2.data.repository
+
+import com.android.intentresolver.v2.coroutines.collectLastValue
+import com.android.intentresolver.v2.shared.model.User
+import com.google.common.truth.Truth.assertThat
+import kotlin.random.Random
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class FakeUserRepositoryTest {
+ private val baseId = Random.nextInt(1000, 2000)
+
+ private val personalUser = User(id = baseId, role = User.Role.PERSONAL)
+ private val cloneUser = User(id = baseId + 1, role = User.Role.CLONE)
+ private val workUser = User(id = baseId + 2, role = User.Role.WORK)
+ private val privateUser = User(id = baseId + 3, role = User.Role.PRIVATE)
+
+ @Test
+ fun init() = runTest {
+ val repo = FakeUserRepository(personalUser, workUser, privateUser)
+
+ val users by collectLastValue(repo.users)
+ assertThat(users).containsExactly(personalUser, workUser, privateUser)
+ }
+
+ @Test
+ fun addUser() = runTest {
+ val repo = FakeUserRepository()
+
+ val users by collectLastValue(repo.users)
+ assertThat(users).isEmpty()
+
+ repo.addUser(personalUser, true)
+ assertThat(users).containsExactly(personalUser)
+
+ repo.addUser(workUser, false)
+ assertThat(users).containsExactly(personalUser, workUser)
+ }
+
+ @Test
+ fun removeUser() = runTest {
+ val repo = FakeUserRepository(personalUser, workUser)
+
+ val users by collectLastValue(repo.users)
+ repo.removeUser(workUser)
+ assertThat(users).containsExactly(personalUser)
+
+ repo.removeUser(personalUser)
+ assertThat(users).isEmpty()
+ }
+
+ @Test
+ fun isAvailable_defaultValue() = runTest {
+ val repo = FakeUserRepository(personalUser, workUser)
+
+ val available by collectLastValue(repo.availability)
+
+ repo.requestState(workUser, false)
+ assertThat(available!![workUser]).isFalse()
+
+ repo.requestState(workUser, true)
+ assertThat(available!![workUser]).isTrue()
+ }
+
+ @Test
+ fun isAvailable() = runTest {
+ val repo = FakeUserRepository(personalUser, workUser)
+
+ val available by collectLastValue(repo.availability)
+ assertThat(available!![workUser]).isTrue()
+
+ repo.requestState(workUser, false)
+ assertThat(available!![workUser]).isFalse()
+
+ repo.requestState(workUser, true)
+ assertThat(available!![workUser]).isTrue()
+ }
+
+ @Test
+ fun isAvailable_addRemove() = runTest {
+ val repo = FakeUserRepository(personalUser, workUser)
+
+ val available by collectLastValue(repo.availability)
+ assertThat(available!![workUser]).isTrue()
+
+ repo.removeUser(workUser)
+ assertThat(available!![workUser]).isNull()
+
+ repo.addUser(workUser, true)
+ assertThat(available!![workUser]).isTrue()
+ }
+}
diff --git a/tests/unit/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt b/tests/unit/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt
index 77f47285..6c61dfd6 100644
--- a/tests/unit/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt
+++ b/tests/unit/src/com/android/intentresolver/v2/data/repository/UserRepositoryImplTest.kt
@@ -8,10 +8,10 @@ import android.os.UserHandle.USER_SYSTEM
import android.os.UserManager
import com.android.intentresolver.mock
import com.android.intentresolver.v2.coroutines.collectLastValue
-import com.android.intentresolver.v2.data.model.User
-import com.android.intentresolver.v2.data.model.User.Role
import com.android.intentresolver.v2.platform.FakeUserManager
import com.android.intentresolver.v2.platform.FakeUserManager.ProfileType
+import com.android.intentresolver.v2.shared.model.User
+import com.android.intentresolver.v2.shared.model.User.Role
import com.android.intentresolver.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -66,30 +66,32 @@ internal class UserRepositoryImplTest {
fun isAvailable() = runTest {
val repo = createUserRepository(userManager)
val work = userState.createProfile(ProfileType.WORK)
+ val workUser = User(work.identifier, Role.WORK)
- val available by collectLastValue(repo.isAvailable(work))
- assertThat(available).isTrue()
+ val available by collectLastValue(repo.availability)
+ assertThat(available?.get(workUser)).isTrue()
userState.setQuietMode(work, true)
- assertThat(available).isFalse()
+ assertThat(available?.get(workUser)).isFalse()
userState.setQuietMode(work, false)
- assertThat(available).isTrue()
+ assertThat(available?.get(workUser)).isTrue()
}
@Test
fun requestState() = runTest {
val repo = createUserRepository(userManager)
val work = userState.createProfile(ProfileType.WORK)
+ val workUser = User(work.identifier, Role.WORK)
- val available by collectLastValue(repo.isAvailable(work))
- assertThat(available).isTrue()
+ val available by collectLastValue(repo.availability)
+ assertThat(available?.get(workUser)).isTrue()
- repo.requestState(work, false)
- assertThat(available).isFalse()
+ repo.requestState(workUser, false)
+ assertThat(available?.get(workUser)).isFalse()
- repo.requestState(work, true)
- assertThat(available).isTrue()
+ repo.requestState(workUser, true)
+ assertThat(available?.get(workUser)).isTrue()
}
@Test(expected = IllegalArgumentException::class)
diff --git a/tests/unit/src/com/android/intentresolver/v2/domain/interactor/UserInteractorTest.kt b/tests/unit/src/com/android/intentresolver/v2/domain/interactor/UserInteractorTest.kt
new file mode 100644
index 00000000..6fa055ef
--- /dev/null
+++ b/tests/unit/src/com/android/intentresolver/v2/domain/interactor/UserInteractorTest.kt
@@ -0,0 +1,179 @@
+/*
+ * 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.intentresolver.v2.domain.interactor
+
+import com.android.intentresolver.v2.coroutines.collectLastValue
+import com.android.intentresolver.v2.data.repository.FakeUserRepository
+import com.android.intentresolver.v2.domain.model.Profile
+import com.android.intentresolver.v2.domain.model.Profile.Type.PERSONAL
+import com.android.intentresolver.v2.domain.model.Profile.Type.PRIVATE
+import com.android.intentresolver.v2.domain.model.Profile.Type.WORK
+import com.android.intentresolver.v2.shared.model.User
+import com.android.intentresolver.v2.shared.model.User.Role
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.random.Random
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class UserInteractorTest {
+ private val baseId = Random.nextInt(1000, 2000)
+
+ private val personalUser = User(id = baseId, role = Role.PERSONAL)
+ private val cloneUser = User(id = baseId + 1, role = Role.CLONE)
+ private val workUser = User(id = baseId + 2, role = Role.WORK)
+ private val privateUser = User(id = baseId + 3, role = Role.PRIVATE)
+
+ @Test
+ fun launchedByProfile(): Unit = runTest {
+ val profileInteractor =
+ UserInteractor(
+ userRepository = FakeUserRepository(personalUser, cloneUser),
+ launchedAs = personalUser.handle
+ )
+
+ val launchedAsProfile by collectLastValue(profileInteractor.launchedAsProfile)
+
+ assertThat(launchedAsProfile).isEqualTo(Profile(PERSONAL, personalUser, cloneUser))
+ }
+
+ @Test
+ fun launchedByProfile_asClone(): Unit = runTest {
+ val profileInteractor =
+ UserInteractor(
+ userRepository = FakeUserRepository(personalUser, cloneUser),
+ launchedAs = cloneUser.handle
+ )
+ val profiles by collectLastValue(profileInteractor.launchedAsProfile)
+
+ assertThat(profiles).isEqualTo(Profile(PERSONAL, personalUser, cloneUser))
+ }
+
+ @Test
+ fun profiles_withPersonal(): Unit = runTest {
+ val profileInteractor =
+ UserInteractor(
+ userRepository = FakeUserRepository(personalUser),
+ launchedAs = personalUser.handle
+ )
+
+ val profiles by collectLastValue(profileInteractor.profiles)
+
+ assertThat(profiles).containsExactly(Profile(PERSONAL, personalUser))
+ }
+
+ @Test
+ fun profiles_addClone(): Unit = runTest {
+ val fakeUserRepo = FakeUserRepository(personalUser)
+ val profileInteractor =
+ UserInteractor(userRepository = fakeUserRepo, launchedAs = personalUser.handle)
+
+ val profiles by collectLastValue(profileInteractor.profiles)
+ assertThat(profiles).containsExactly(Profile(PERSONAL, personalUser))
+
+ fakeUserRepo.addUser(cloneUser, available = true)
+ assertThat(profiles).containsExactly(Profile(PERSONAL, personalUser, cloneUser))
+ }
+
+ @Test
+ fun profiles_withPersonalAndClone(): Unit = runTest {
+ val profileInteractor =
+ UserInteractor(
+ userRepository = FakeUserRepository(personalUser, cloneUser),
+ launchedAs = personalUser.handle
+ )
+ val profiles by collectLastValue(profileInteractor.profiles)
+
+ assertThat(profiles).containsExactly(Profile(PERSONAL, personalUser, cloneUser))
+ }
+
+ @Test
+ fun profiles_withAllSupportedTypes(): Unit = runTest {
+ val profileInteractor =
+ UserInteractor(
+ userRepository = FakeUserRepository(personalUser, cloneUser, workUser, privateUser),
+ launchedAs = personalUser.handle
+ )
+ val profiles by collectLastValue(profileInteractor.profiles)
+
+ assertThat(profiles)
+ .containsExactly(
+ Profile(PERSONAL, personalUser, cloneUser),
+ Profile(WORK, workUser),
+ Profile(PRIVATE, privateUser)
+ )
+ }
+
+ @Test
+ fun profiles_preservesIterationOrder(): Unit = runTest {
+ val profileInteractor =
+ UserInteractor(
+ userRepository = FakeUserRepository(workUser, cloneUser, privateUser, personalUser),
+ launchedAs = personalUser.handle
+ )
+
+ val profiles by collectLastValue(profileInteractor.profiles)
+
+ assertThat(profiles)
+ .containsExactly(
+ Profile(WORK, workUser),
+ Profile(PRIVATE, privateUser),
+ Profile(PERSONAL, personalUser, cloneUser),
+ )
+ }
+
+ @Test
+ fun isAvailable_defaultValue() = runTest {
+ val userRepo = FakeUserRepository(personalUser)
+ userRepo.addUser(workUser, false)
+
+ val profileInteractor =
+ UserInteractor(userRepository = userRepo, launchedAs = personalUser.handle)
+ val personalAvailable by collectLastValue(profileInteractor.isAvailable(PERSONAL))
+ val workAvailable by collectLastValue(profileInteractor.isAvailable(WORK))
+
+ assertWithMessage("personalAvailable").that(personalAvailable!!).isTrue()
+
+ assertWithMessage("workAvailable").that(workAvailable!!).isFalse()
+ }
+
+ @Test
+ fun isAvailable() = runTest {
+ val userRepo = FakeUserRepository(workUser, personalUser)
+ val profileInteractor =
+ UserInteractor(userRepository = userRepo, launchedAs = personalUser.handle)
+ val workAvailable by collectLastValue(profileInteractor.isAvailable(WORK))
+
+ // Default state is enabled in FakeUserManager
+ assertWithMessage("workAvailable").that(workAvailable).isTrue()
+
+ // Making user unavailable makes profile unavailable
+ userRepo.requestState(workUser, false)
+ assertWithMessage("workAvailable").that(workAvailable).isFalse()
+
+ // Making user available makes profile available again
+ userRepo.requestState(workUser, true)
+ assertWithMessage("workAvailable").that(workAvailable).isTrue()
+
+ // When a user is removed availability should update to false
+ userRepo.removeUser(workUser)
+ assertWithMessage("workAvailable").that(workAvailable).isFalse()
+ }
+}