summaryrefslogtreecommitdiff
path: root/java/tests
diff options
context:
space:
mode:
author 1 <mrcasey@google.com> 2023-05-01 21:09:50 +0000
committer 1 <mrcasey@google.com> 2023-05-08 16:36:53 +0000
commit7cc1eb3e660decca55d42105835d7935ef444613 (patch)
treea12ceb303f0069199d52c88f3d3b2b011ef96648 /java/tests
parentd572ef6c2ba73880edecd66c86835e78763d9400 (diff)
Use a ViewState to preserve refinement state
Refinement manager keeps its own state across config changes so that it can receive callbacks on the ResultReceiver. ChooserActivity can watch for the state of refinement completion and launch the refined TargetInfo if refinement succeeds, otherwise just finish(). If onResume happens when refinement is in progress, ChooserActivity will finish, as this is expected to be abandonment of the refinement process without a result. But, onResume also happens during a config change during refinement, and the activity must not finish in those cases. The logic for this is contained within ChooserRefinementManager, with ChooserActivity only needing to pass along a couple of lifecycle events. Add a bunch of test coverage to new and old functionality. Also fix some kotlin nullability errors in TargetInfo tests that sysui studio was complaining about. Bug: 279514914 Test: atest ChooserRefinementManagerTest Test: manually test the refinement flow in Photos, both completing and cancelling the process, rotating at various points. Change-Id: I2b22155894e29b062c78b4af20e8fe0683d40bea
Diffstat (limited to 'java/tests')
-rw-r--r--java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt225
-rw-r--r--java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt34
-rw-r--r--java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt10
3 files changed, 225 insertions, 44 deletions
diff --git a/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt b/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
index 50c37c7f..bd355c86 100644
--- a/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
+++ b/java/tests/src/com/android/intentresolver/ChooserRefinementManagerTest.kt
@@ -16,46 +16,227 @@
package com.android.intentresolver
-import android.content.Context
+import android.app.Activity
+import android.app.Application
import android.content.Intent
import android.content.IntentSender
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.os.ResultReceiver
+import androidx.lifecycle.Observer
+import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.intentresolver.ChooserRefinementManager.RefinementCompletion
+import com.android.intentresolver.chooser.ImmutableTargetInfo
import com.android.intentresolver.chooser.TargetInfo
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
-import java.util.function.Consumer
-import org.junit.Assert.assertEquals
@RunWith(AndroidJUnit4::class)
+@UiThreadTest
class ChooserRefinementManagerTest {
- @Test
- fun testMaybeHandleSelection() {
- val intentSender = mock<IntentSender>()
- val refinementManager = ChooserRefinementManager(
- mock<Context>(),
- intentSender,
- Consumer<TargetInfo>{},
- Runnable{})
-
- val intents = listOf(Intent(Intent.ACTION_VIEW), Intent(Intent.ACTION_EDIT))
- val targetInfo = mock<TargetInfo>{
- whenever(allSourceIntents).thenReturn(intents)
+ private val refinementManager = ChooserRefinementManager()
+ private val intentSender = mock<IntentSender>()
+ private val application = mock<Application>()
+ private val exampleSourceIntents =
+ listOf(Intent(Intent.ACTION_VIEW), Intent(Intent.ACTION_EDIT))
+ private val exampleTargetInfo =
+ ImmutableTargetInfo.newBuilder().setAllSourceIntents(exampleSourceIntents).build()
+
+ private val completionObserver =
+ object : Observer<RefinementCompletion> {
+ val failureCountDown = CountDownLatch(1)
+ val successCountDown = CountDownLatch(1)
+ var latestTargetInfo: TargetInfo? = null
+
+ override fun onChanged(completion: RefinementCompletion) {
+ if (completion.consume()) {
+ val targetInfo = completion.targetInfo
+ if (targetInfo == null) {
+ failureCountDown.countDown()
+ } else {
+ latestTargetInfo = targetInfo
+ successCountDown.countDown()
+ }
+ }
+ }
}
- refinementManager.maybeHandleSelection(targetInfo)
+ /** Synchronously executes post() calls. */
+ private class FakeHandler(looper: Looper) : Handler(looper) {
+ override fun sendMessageAtTime(msg: Message, uptimeMillis: Long): Boolean {
+ dispatchMessage(msg)
+ return true
+ }
+ }
+
+ @Before
+ fun setup() {
+ refinementManager.refinementCompletion.observeForever(completionObserver)
+ }
+
+ @Test
+ fun testTypicalRefinementFlow() {
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ exampleTargetInfo,
+ intentSender,
+ application,
+ FakeHandler(Looper.myLooper())
+ )
+ )
+ .isTrue()
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
- Mockito.verify(intentSender).sendIntent(
- any(), eq(0), intentCaptor.capture(), eq(null), eq(null))
+ Mockito.verify(intentSender)
+ .sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null))
val intent = intentCaptor.value
- assertEquals(intents[0], intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java))
+ assertThat(intent?.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java))
+ .isEqualTo(exampleSourceIntents[0])
val alternates =
- intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java)
- assertEquals(1, alternates?.size)
- assertEquals(intents[1], alternates?.get(0))
+ intent?.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java)
+ assertThat(alternates?.size).isEqualTo(1)
+ assertThat(alternates?.get(0)).isEqualTo(exampleSourceIntents[1])
+
+ // Complete the refinement
+ val receiver =
+ intent?.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java)
+ val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) }
+ receiver?.send(Activity.RESULT_OK, bundle)
+
+ assertThat(completionObserver.successCountDown.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+ assertThat(completionObserver.latestTargetInfo?.resolvedIntent?.action)
+ .isEqualTo(Intent.ACTION_VIEW)
+ }
+
+ @Test
+ fun testRefinementCancelled() {
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ exampleTargetInfo,
+ intentSender,
+ application,
+ FakeHandler(Looper.myLooper())
+ )
+ )
+ .isTrue()
+
+ val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+ Mockito.verify(intentSender)
+ .sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null))
+
+ val intent = intentCaptor.value
+
+ // Complete the refinement
+ val receiver =
+ intent?.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java)
+ val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) }
+ receiver?.send(Activity.RESULT_CANCELED, bundle)
+
+ assertThat(completionObserver.failureCountDown.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ @Test
+ fun testMaybeHandleSelection_noSourceIntents() {
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ ImmutableTargetInfo.newBuilder().build(),
+ intentSender,
+ application,
+ FakeHandler(Looper.myLooper())
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun testMaybeHandleSelection_suspended() {
+ val targetInfo =
+ ImmutableTargetInfo.newBuilder()
+ .setAllSourceIntents(exampleSourceIntents)
+ .setIsSuspended(true)
+ .build()
+
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ targetInfo,
+ intentSender,
+ application,
+ FakeHandler(Looper.myLooper())
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun testMaybeHandleSelection_noIntentSender() {
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ exampleTargetInfo,
+ /* IntentSender */ null,
+ application,
+ FakeHandler(Looper.myLooper())
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun testConfigurationChangeDuringRefinement() {
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ exampleTargetInfo,
+ intentSender,
+ application,
+ FakeHandler(Looper.myLooper())
+ )
+ )
+ .isTrue()
+
+ refinementManager.onActivityStop(/* config changing = */ true)
+ refinementManager.onActivityResume()
+
+ assertThat(completionObserver.failureCountDown.count).isEqualTo(1)
+ }
+
+ @Test
+ fun testResumeDuringRefinement() {
+ assertThat(
+ refinementManager.maybeHandleSelection(
+ exampleTargetInfo,
+ intentSender,
+ application,
+ FakeHandler(Looper.myLooper()!!)
+ )
+ )
+ .isTrue()
+
+ refinementManager.onActivityStop(/* config changing = */ false)
+ // Resume during refinement but not during a config change, so finish the activity.
+ refinementManager.onActivityResume()
+
+ // Call should be synchronous, don't need to await for this one.
+ assertThat(completionObserver.failureCountDown.count).isEqualTo(0)
+ }
+
+ @Test
+ fun testRefinementCompletion() {
+ val refinementCompletion = RefinementCompletion(exampleTargetInfo)
+ assertThat(refinementCompletion.targetInfo).isEqualTo(exampleTargetInfo)
+ assertThat(refinementCompletion.consume()).isTrue()
+ assertThat(refinementCompletion.targetInfo).isEqualTo(exampleTargetInfo)
+
+ // can only consume once.
+ assertThat(refinementCompletion.consume()).isFalse()
}
}
diff --git a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
index cebccaae..504cfd97 100644
--- a/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
+++ b/java/tests/src/com/android/intentresolver/chooser/ImmutableTargetInfoTest.kt
@@ -222,8 +222,8 @@ class ImmutableTargetInfoTest {
.build()
val info = originalInfo.tryToCloneWithAppliedRefinement(refinementIntent)
- assertThat(info.baseIntentToSend.getBooleanExtra("ORIGINAL", false)).isTrue()
- assertThat(info.baseIntentToSend.getBooleanExtra("REFINEMENT", false)).isTrue()
+ assertThat(info?.baseIntentToSend?.getBooleanExtra("ORIGINAL", false)).isTrue()
+ assertThat(info?.baseIntentToSend?.getBooleanExtra("REFINEMENT", false)).isTrue()
}
@Test
@@ -245,9 +245,9 @@ class ImmutableTargetInfoTest {
val info = infoWithReferrerFillIn.tryToCloneWithAppliedRefinement(refinementIntent)
- assertThat(info.baseIntentToSend.getPackage()).isEqualTo("original") // Set all along.
- assertThat(info.baseIntentToSend.action).isEqualTo("REFINE_ME") // Refinement wins.
- assertThat(info.baseIntentToSend.type).isEqualTo("test/referrer") // Left for referrer.
+ assertThat(info?.baseIntentToSend?.getPackage()).isEqualTo("original") // Set all along.
+ assertThat(info?.baseIntentToSend?.action).isEqualTo("REFINE_ME") // Refinement wins.
+ assertThat(info?.baseIntentToSend?.type).isEqualTo("test/referrer") // Left for referrer.
}
@Test
@@ -266,18 +266,18 @@ class ImmutableTargetInfoTest {
.build()
val refined1 = originalInfo.tryToCloneWithAppliedRefinement(refinementIntent1)
- val refined2 = refined1.tryToCloneWithAppliedRefinement(refinementIntent2) // Cloned clone.
+ val refined2 = refined1?.tryToCloneWithAppliedRefinement(refinementIntent2) // Cloned clone.
// Both clones get the same values filled in from the referrer intent.
- assertThat(refined1.baseIntentToSend.getStringExtra("TEST")).isEqualTo("REFERRER")
- assertThat(refined2.baseIntentToSend.getStringExtra("TEST")).isEqualTo("REFERRER")
+ assertThat(refined1?.baseIntentToSend?.getStringExtra("TEST")).isEqualTo("REFERRER")
+ assertThat(refined2?.baseIntentToSend?.getStringExtra("TEST")).isEqualTo("REFERRER")
// Each clone has the respective value that was set in their own refinement request.
- assertThat(refined1.baseIntentToSend.getStringExtra("TEST1")).isEqualTo("1")
- assertThat(refined2.baseIntentToSend.getStringExtra("TEST2")).isEqualTo("2")
+ assertThat(refined1?.baseIntentToSend?.getStringExtra("TEST1")).isEqualTo("1")
+ assertThat(refined2?.baseIntentToSend?.getStringExtra("TEST2")).isEqualTo("2")
// The clones don't have the data from each other's refinements, even though the intent
// field is empty (thus able to be populated by filling-in).
- assertThat(refined1.baseIntentToSend.getStringExtra("TEST2")).isNull()
- assertThat(refined2.baseIntentToSend.getStringExtra("TEST1")).isNull()
+ assertThat(refined1?.baseIntentToSend?.getStringExtra("TEST2")).isNull()
+ assertThat(refined2?.baseIntentToSend?.getStringExtra("TEST1")).isNull()
}
@Test
@@ -301,15 +301,15 @@ class ImmutableTargetInfoTest {
refinement.putExtra("refinement", true)
val refinedResult = originalInfo.tryToCloneWithAppliedRefinement(refinement)
- assertThat(refinedResult.baseIntentToSend.getBooleanExtra("refinement", false)).isTrue()
- assertThat(refinedResult.baseIntentToSend.getBooleanExtra("targetAlternate", false))
+ assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("refinement", false)).isTrue()
+ assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("targetAlternate", false))
.isTrue()
// None of the other source intents got merged in (not even the later one that matched):
- assertThat(refinedResult.baseIntentToSend.getBooleanExtra("originalIntent", false))
+ assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("originalIntent", false))
.isFalse()
- assertThat(refinedResult.baseIntentToSend.getBooleanExtra("mismatchedAlternate", false))
+ assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("mismatchedAlternate", false))
.isFalse()
- assertThat(refinedResult.baseIntentToSend.getBooleanExtra("extraMatch", false)).isFalse()
+ assertThat(refinedResult?.baseIntentToSend?.getBooleanExtra("extraMatch", false)).isFalse()
}
@Test
diff --git a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt b/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
index 886e32df..f9d3dd96 100644
--- a/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
+++ b/java/tests/src/com/android/intentresolver/chooser/TargetInfoTest.kt
@@ -234,15 +234,15 @@ class TargetInfoTest {
val refinedResult = originalInfo.tryToCloneWithAppliedRefinement(refinement)
// Note `DisplayResolveInfo` targets merge refinements directly into their `resolvedIntent`.
- assertThat(refinedResult.resolvedIntent.getBooleanExtra("refinement", false)).isTrue()
- assertThat(refinedResult.resolvedIntent.getBooleanExtra("targetAlternate", false))
+ assertThat(refinedResult?.resolvedIntent?.getBooleanExtra("refinement", false)).isTrue()
+ assertThat(refinedResult?.resolvedIntent?.getBooleanExtra("targetAlternate", false))
.isTrue()
// None of the other source intents got merged in (not even the later one that matched):
- assertThat(refinedResult.resolvedIntent.getBooleanExtra("originalIntent", false))
+ assertThat(refinedResult?.resolvedIntent?.getBooleanExtra("originalIntent", false))
.isFalse()
- assertThat(refinedResult.resolvedIntent.getBooleanExtra("mismatchedAlternate", false))
+ assertThat(refinedResult?.resolvedIntent?.getBooleanExtra("mismatchedAlternate", false))
.isFalse()
- assertThat(refinedResult.resolvedIntent.getBooleanExtra("extraMatch", false)).isFalse()
+ assertThat(refinedResult?.resolvedIntent?.getBooleanExtra("extraMatch", false)).isFalse()
}
@Test