summaryrefslogtreecommitdiff
path: root/java/tests
diff options
context:
space:
mode:
author Joshua Trask <joshtrask@google.com> 2023-10-04 15:31:47 +0000
committer Joshua Trask <joshtrask@google.com> 2023-10-04 15:37:31 +0000
commit989de76169699beee2c92a8ce41bc636896a66b6 (patch)
treed51d1394d24abb724185ecab406f1745bb0315f6 /java/tests
parent2bcce186d5b4879e33aedd6bbac7f5b0b4ec9cd8 (diff)
`v2` boilerplate for ongoing empty-state work.
Just cutting over to our new experiment infrastructure before I continue porting over the changes that were originally prototyped in ag/24516421. Bug: 302311217 Test: IntentResolverUnitTests Change-Id: Ied1843bee2be6ffb42ba4f539f6168a9d07a77d9
Diffstat (limited to 'java/tests')
-rw-r--r--java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt282
-rw-r--r--java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt112
2 files changed, 394 insertions, 0 deletions
diff --git a/java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt b/java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt
new file mode 100644
index 00000000..f1af9790
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/v2/MultiProfilePagerAdapterTest.kt
@@ -0,0 +1,282 @@
+/*
+ * 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.intentresolver.v2
+
+import android.os.UserHandle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ListView
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.intentresolver.MultiProfilePagerAdapter.PROFILE_PERSONAL
+import com.android.intentresolver.MultiProfilePagerAdapter.PROFILE_WORK
+import com.android.intentresolver.R
+import com.android.intentresolver.ResolverListAdapter
+import com.android.intentresolver.any
+import com.android.intentresolver.emptystate.EmptyStateProvider
+import com.android.intentresolver.mock
+import com.android.intentresolver.whenever
+import com.google.common.collect.ImmutableList
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import java.util.function.Supplier
+import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+class MultiProfilePagerAdapterTest {
+ private val PERSONAL_USER_HANDLE = UserHandle.of(10)
+ private val WORK_USER_HANDLE = UserHandle.of(20)
+
+ private val context = InstrumentationRegistry.getInstrumentation().getContext()
+ private val inflater = Supplier {
+ LayoutInflater.from(context).inflate(R.layout.resolver_list_per_profile, null, false)
+ as ViewGroup
+ }
+
+ @Test
+ fun testSinglePageProfileAdapter() {
+ val personalListAdapter =
+ mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(personalListAdapter),
+ object : EmptyStateProvider {},
+ { false },
+ PROFILE_PERSONAL,
+ null,
+ null,
+ inflater,
+ { Optional.empty() }
+ )
+ assertThat(pagerAdapter.count).isEqualTo(1)
+ assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
+ assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
+ assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.inactiveListAdapter).isNull()
+ assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.workListAdapter).isNull()
+ assertThat(pagerAdapter.itemCount).isEqualTo(1)
+ // TODO: consider covering some of the package-private methods (and making them public?).
+ // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter.
+ }
+
+ @Test
+ fun testTwoProfilePagerAdapter() {
+ val personalListAdapter =
+ mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
+ val workListAdapter =
+ mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(personalListAdapter, workListAdapter),
+ object : EmptyStateProvider {},
+ { false },
+ PROFILE_PERSONAL,
+ WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
+ null,
+ inflater,
+ { Optional.empty() }
+ )
+ assertThat(pagerAdapter.count).isEqualTo(2)
+ assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
+ assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
+ assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.getAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
+ assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.inactiveListAdapter).isSameInstanceAs(workListAdapter)
+ assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
+ assertThat(pagerAdapter.itemCount).isEqualTo(2)
+ // TODO: consider covering some of the package-private methods (and making them public?).
+ // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter;
+ // especially matching profiles to ListViews?
+ // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
+ // page changes. Currently there's no API to change the selected page directly; that's
+ // only possible through manipulation of the bound ViewPager.
+ }
+
+ @Test
+ fun testTwoProfilePagerAdapter_workIsDefault() {
+ val personalListAdapter =
+ mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE) }
+ val workListAdapter =
+ mock<ResolverListAdapter> { whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE) }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(personalListAdapter, workListAdapter),
+ object : EmptyStateProvider {},
+ { false },
+ PROFILE_WORK, // <-- This test specifically requests we start on work profile.
+ WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
+ null,
+ inflater,
+ { Optional.empty() }
+ )
+ assertThat(pagerAdapter.count).isEqualTo(2)
+ assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_WORK)
+ assertThat(pagerAdapter.currentUserHandle).isEqualTo(WORK_USER_HANDLE)
+ assertThat(pagerAdapter.getAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.getAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
+ assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(workListAdapter)
+ assertThat(pagerAdapter.inactiveListAdapter).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
+ assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
+ assertThat(pagerAdapter.itemCount).isEqualTo(2)
+ // TODO: consider covering some of the package-private methods (and making them public?).
+ // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
+ // page changes. Currently there's no API to change the selected page directly; that's
+ // only possible through manipulation of the bound ViewPager.
+ }
+
+ @Test
+ fun testBottomPaddingDelegate_default() {
+ val container =
+ mock<View> {
+ whenever(getPaddingLeft()).thenReturn(1)
+ whenever(getPaddingTop()).thenReturn(2)
+ whenever(getPaddingRight()).thenReturn(3)
+ whenever(getPaddingBottom()).thenReturn(4)
+ }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(),
+ object : EmptyStateProvider {},
+ { false },
+ PROFILE_PERSONAL,
+ null,
+ null,
+ inflater,
+ { Optional.empty() }
+ )
+ pagerAdapter.setupContainerPadding(container)
+ verify(container, never()).setPadding(any(), any(), any(), any())
+ }
+
+ @Test
+ fun testBottomPaddingDelegate_override() {
+ val container =
+ mock<View> {
+ whenever(getPaddingLeft()).thenReturn(1)
+ whenever(getPaddingTop()).thenReturn(2)
+ whenever(getPaddingRight()).thenReturn(3)
+ whenever(getPaddingBottom()).thenReturn(4)
+ }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(),
+ object : EmptyStateProvider {},
+ { false },
+ PROFILE_PERSONAL,
+ null,
+ null,
+ inflater,
+ { Optional.of(42) }
+ )
+ pagerAdapter.setupContainerPadding(container)
+ verify(container).setPadding(1, 2, 3, 42)
+ }
+
+ @Test
+ fun testPresumedQuietModeEmptyStateForWorkProfile_whenQuiet() {
+ // TODO: this is "presumed" because the conditions to determine whether we "should" show an
+ // empty state aren't enforced to align with the conditions when we actually *would* -- I
+ // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
+ val personalListAdapter =
+ mock<ResolverListAdapter> {
+ whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE)
+ whenever(getUnfilteredCount()).thenReturn(1)
+ }
+ val workListAdapter =
+ mock<ResolverListAdapter> {
+ whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE)
+ whenever(getUnfilteredCount()).thenReturn(1)
+ }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(personalListAdapter, workListAdapter),
+ object : EmptyStateProvider {},
+ { true }, // <-- Work mode is quiet.
+ PROFILE_WORK,
+ WORK_USER_HANDLE,
+ null,
+ inflater,
+ { Optional.empty() }
+ )
+ assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isTrue()
+ assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
+ }
+
+ @Test
+ fun testPresumedQuietModeEmptyStateForWorkProfile_notWhenNotQuiet() {
+ // TODO: this is "presumed" because the conditions to determine whether we "should" show an
+ // empty state aren't enforced to align with the conditions when we actually *would* -- I
+ // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
+ val personalListAdapter =
+ mock<ResolverListAdapter> {
+ whenever(getUserHandle()).thenReturn(PERSONAL_USER_HANDLE)
+ whenever(getUnfilteredCount()).thenReturn(1)
+ }
+ val workListAdapter =
+ mock<ResolverListAdapter> {
+ whenever(getUserHandle()).thenReturn(WORK_USER_HANDLE)
+ whenever(getUnfilteredCount()).thenReturn(1)
+ }
+ val pagerAdapter =
+ MultiProfilePagerAdapter(
+ { listAdapter: ResolverListAdapter -> listAdapter },
+ { listView: ListView, bindAdapter: ResolverListAdapter ->
+ listView.setAdapter(bindAdapter)
+ },
+ ImmutableList.of(personalListAdapter, workListAdapter),
+ object : EmptyStateProvider {},
+ { false }, // <-- Work mode is not quiet.
+ PROFILE_WORK,
+ WORK_USER_HANDLE,
+ null,
+ inflater,
+ { Optional.empty() }
+ )
+ assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isFalse()
+ assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
+ }
+}
diff --git a/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt b/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
new file mode 100644
index 00000000..12943cd7
--- /dev/null
+++ b/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.intentresolver.v2.emptystate
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+class EmptyStateUiHelperTest {
+ private val context = InstrumentationRegistry.getInstrumentation().getContext()
+
+ lateinit var rootContainer: ViewGroup
+ lateinit var emptyStateTitleView: View
+ lateinit var emptyStateSubtitleView: View
+ lateinit var emptyStateButtonView: View
+ lateinit var emptyStateProgressView: View
+ lateinit var emptyStateDefaultTextView: View
+ lateinit var emptyStateContainerView: View
+ lateinit var emptyStateRootView: View
+ lateinit var emptyStateUiHelper: EmptyStateUiHelper
+
+ @Before
+ fun setup() {
+ rootContainer = FrameLayout(context)
+ LayoutInflater.from(context)
+ .inflate(
+ com.android.intentresolver.R.layout.resolver_list_per_profile,
+ rootContainer,
+ true
+ )
+ emptyStateRootView =
+ rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state)
+ emptyStateTitleView =
+ rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_title)
+ emptyStateSubtitleView =
+ rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_subtitle)
+ emptyStateButtonView =
+ rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_button)
+ emptyStateProgressView =
+ rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_progress)
+ emptyStateDefaultTextView = rootContainer.requireViewById(com.android.internal.R.id.empty)
+ emptyStateContainerView =
+ rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_container)
+ emptyStateUiHelper = EmptyStateUiHelper(rootContainer)
+ }
+
+ @Test
+ fun testResetViewVisibilities() {
+ // First set each view's visibility to differ from the expected "reset" state so we can then
+ // assert that they're all reset afterward.
+ // TODO: for historic reasons "reset" doesn't cover `emptyStateContainerView`; should it?
+ emptyStateRootView.visibility = View.GONE
+ emptyStateTitleView.visibility = View.GONE
+ emptyStateSubtitleView.visibility = View.GONE
+ emptyStateButtonView.visibility = View.VISIBLE
+ emptyStateProgressView.visibility = View.VISIBLE
+ emptyStateDefaultTextView.visibility = View.VISIBLE
+
+ emptyStateUiHelper.resetViewVisibilities()
+
+ assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
+ assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowSpinner() {
+ emptyStateTitleView.visibility = View.VISIBLE
+ emptyStateButtonView.visibility = View.VISIBLE
+ emptyStateProgressView.visibility = View.GONE
+ emptyStateDefaultTextView.visibility = View.VISIBLE
+
+ emptyStateUiHelper.showSpinner()
+
+ // TODO: should this cover any other views? Subtitle?
+ assertThat(emptyStateTitleView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(emptyStateProgressView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testHide() {
+ emptyStateRootView.visibility = View.VISIBLE
+
+ emptyStateUiHelper.hide()
+
+ assertThat(emptyStateRootView.visibility).isEqualTo(View.GONE)
+ }
+}