VoiceInteraction: Fix testVoiceInteractorDestroy flakiness.
The test activity checks that the interactor is properly marked destroyed in
the callback that executes when the interactor is destroyed. There is a race
condition because there's no synchronization between
VoiceInteractor.destroy() and the accessors on VoiceInteractor/Activity.
With this change, the accessors are polled until the values update.
Also moves these checks out of the onDestroyedCallback; there's no reason to
keep them there and it's a bit more readable with them moved out.
Bug: 178325554
Test: atest CtsVoiceInteractionTestCases --iterations
Change-Id: I000ace2f207473eafad3e2ca48a6b279883a6d0a
diff --git a/tests/tests/voiceinteraction/testapp/Android.bp b/tests/tests/voiceinteraction/testapp/Android.bp
index 94e1a0e..a01e43f 100644
--- a/tests/tests/voiceinteraction/testapp/Android.bp
+++ b/tests/tests/voiceinteraction/testapp/Android.bp
@@ -15,7 +15,11 @@
android_test_helper_app {
name: "CtsVoiceInteractionApp",
defaults: ["cts_support_defaults"],
- static_libs: ["CtsVoiceInteractionCommon", "androidx.core_core"],
+ static_libs: [
+ "CtsVoiceInteractionCommon",
+ "androidx.core_core",
+ "compatibility-device-util-axt",
+ ],
srcs: ["src/**/*.java"],
sdk_version: "test_current",
// Tag this module as a cts test artifact
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
index b64c734..5f36d8960 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
@@ -29,6 +29,10 @@
import androidx.annotation.NonNull;
+import com.android.compatibility.common.util.PollingCheck;
+
+import com.google.common.truth.Truth;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -118,19 +122,29 @@
}
private void detectDestroyedInteractor(@NonNull RemoteCallback callback) {
- final Bundle result = new Bundle();
final CountDownLatch latch = new CountDownLatch(1);
-
final VoiceInteractor interactor = getVoiceInteractor();
- interactor.registerOnDestroyedCallback(AsyncTask.THREAD_POOL_EXECUTOR, () -> {
- if (interactor.isDestroyed() && getVoiceInteractor() == null) {
- result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
- }
- latch.countDown();
- });
-
+ interactor.registerOnDestroyedCallback(AsyncTask.THREAD_POOL_EXECUTOR, latch::countDown);
Utils.await(latch);
+ try {
+ // Check that the interactor is properly marked destroyed. Polls the values since
+ // there's no synchronization between destroy() and these methods.
+ long pollingTimeoutMs = 3000;
+ PollingCheck.check(
+ "onDestroyedCallback called but interactor isn't destroyed",
+ pollingTimeoutMs,
+ interactor::isDestroyed);
+ PollingCheck.check(
+ "onDestroyedCallback called but activity still has an interactor",
+ pollingTimeoutMs,
+ () -> getVoiceInteractor() == null);
+ } catch (Exception e) {
+ Truth.assertWithMessage("Unexpected exception: " + e).fail();
+ }
+
+ final Bundle result = new Bundle();
+ result.putBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT, true);
Log.v(TAG, "detectDestroyedInteractor(): " + Utils.toBundleString(result));
callback.sendResult(result);
}