Cleanup InputMethodStatsTest
This removes some of the duplication for setting up and verifying events
in InputMethodStatsTest.
Test: atest android.view.inputmethod.cts.InputMethodStatsTest
Bug: 271426908
Change-Id: I7dd95b58a50a7244735bfaff479da1342cc02e9b
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStatsTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStatsTest.java
index 9d3281a..2e31057 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStatsTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStatsTest.java
@@ -31,6 +31,7 @@
import android.platform.test.annotations.AppModeSdkSandbox;
import android.view.WindowInsets;
import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
+import android.view.WindowManager;
import android.view.inputmethod.cts.util.EndToEndImeTestBase;
import android.view.inputmethod.cts.util.MetricsRecorder;
import android.view.inputmethod.cts.util.TestActivity;
@@ -39,6 +40,7 @@
import android.widget.EditText;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -89,11 +91,28 @@
mPkgName = "";
}
- private TestActivity createTestActivity(final int windowFlags) {
- return TestActivity.startSync(activity -> createLayout(windowFlags, activity));
+ /**
+ * Creates and launches a test activity.
+ *
+ * @param mode the {@link WindowManager.LayoutParams#softInputMode softInputMode} for the
+ * activity.
+ *
+ * @return the created activity.
+ */
+ private TestActivity createTestActivity(final int mode) {
+ return TestActivity.startSync(activity -> createLayout(mode, activity));
}
- private LinearLayout createLayout(final int windowFlags, final Activity activity) {
+ /**
+ * Creates a linear layout with one EditText.
+ *
+ * @param mode the {@link WindowManager.LayoutParams#softInputMode softInputMode} for the
+ * activity.
+ * @param activity the activity to create the layout for.
+ *
+ * @return the created layout.
+ */
+ private LinearLayout createLayout(final int mode, final Activity activity) {
final var layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
@@ -101,7 +120,7 @@
editText.setText("Editable");
layout.addView(editText);
editText.requestFocus();
- activity.getWindow().setSoftInputMode(windowFlags);
+ activity.getWindow().setSoftInputMode(mode);
return layout;
}
@@ -109,16 +128,16 @@
* Waits for the given inset type to be controllable on the given activity's
* {@link android.view.WindowInsetsController}.
*
- * @implNote
- * This is used to avoid the case where {@link android.view.InsetsController#show(int)}
+ * @param type the inset type waiting to be controllable.
+ * @param activity the activity whose Window Insets Controller to wait on.
+ *
+ * @implNote This is used to avoid the case where
+ * {@link android.view.InsetsController#show(int)}
* is called before IME insets control is available, starting a more complex flow which is
* currently harder to track with the {@link com.android.server.inputmethod.ImeTrackerService}
* system.
*
* TODO(b/263069667): Remove this method when the ImeInsetsSourceConsumer show flow is fixed.
- *
- * @param type the inset type waiting to be controllable.
- * @param activity the activity whose Window Insets Controller to wait on.
*/
private void awaitControl(final int type, final Activity activity) {
final var latch = new CountDownLatch(1);
@@ -151,72 +170,18 @@
*/
@Test
public void testClientShowImeRequestFinished() throws Throwable {
- // Create mockImeSession to decouple from real IMEs,
- // and enable calling expectImeVisible.
- try (var imeSession = MockImeSession.create(
- mInstrumentation.getContext(),
- mInstrumentation.getUiAutomation(),
- new ImeSettings.Builder())) {
- // Wait for any outstanding IME requests to finish, to not interfere with test.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Setup Failed: There should be no pending IME requests present when the "
- + "test starts.");
+ verifyLogging(true /* show */, ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
+ (imeSession, activity) -> {
+ awaitControl(WindowInsets.Type.ime(), activity);
+ expectImeInvisible(TIMEOUT);
- MetricsRecorder.uploadConfigForPushedAtomWithUid(mPkgName,
- AtomsProto.Atom.IME_REQUEST_FINISHED_FIELD_NUMBER,
- false /* useUidAttributionChain */);
+ TestUtils.runOnMainSync(() -> activity.getWindow()
+ .getDecorView()
+ .getWindowInsetsController()
+ .show(WindowInsets.Type.ime()));
- final var activity = createTestActivity(SOFT_INPUT_STATE_UNCHANGED);
- awaitControl(WindowInsets.Type.ime(), activity);
- expectImeInvisible(TIMEOUT);
-
- TestUtils.runOnMainSync(() -> activity.getWindow()
- .getDecorView()
- .getWindowInsetsController()
- .show(WindowInsets.Type.ime()));
-
- expectImeVisible(TIMEOUT);
- // Wait for any outstanding IME requests to finish, to capture all atoms successfully.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Error: Pending IME requests took too long, likely timing out.");
-
- final var data = MetricsRecorder.getEventMetricDataList();
- assertWithMessage("Number of atoms logged")
- .that(data.size())
- .isAtLeast(1);
-
- try {
- int successfulAtoms = 0;
- for (int i = 0; i < data.size(); i++) {
- final var atom = data.get(i).atom;
- assertThat(atom).isNotNull();
-
- final var imeRequestFinished = atom.getImeRequestFinished();
- assertThat(imeRequestFinished).isNotNull();
-
- // Skip cancelled requests.
- if (imeRequestFinished.status == ImeProtoEnums.STATUS_CANCEL) continue;
-
- successfulAtoms++;
-
- assertWithMessage("Ime Request type")
- .that(imeRequestFinished.type)
- .isEqualTo(ImeProtoEnums.TYPE_SHOW);
- assertWithMessage("Ime Request status")
- .that(imeRequestFinished.status)
- .isEqualTo(ImeProtoEnums.STATUS_SUCCESS);
- assertWithMessage("Ime Request origin")
- .that(imeRequestFinished.origin)
- .isEqualTo(ImeProtoEnums.ORIGIN_CLIENT_SHOW_SOFT_INPUT);
- }
-
- assertWithMessage("Number of successful atoms logged")
- .that(successfulAtoms)
- .isAtLeast(1);
- } catch (AssertionError e) {
- throw new AssertionError(e.getMessage() + "\natoms data:\n" + data, e);
- }
- }
+ expectImeVisible(TIMEOUT);
+ });
}
/**
@@ -224,85 +189,15 @@
*/
@Test
public void testClientHideImeRequestFinished() throws Exception {
- // Create mockImeSession to decouple from real IMEs,
- // and enable calling expectImeVisible and expectImeInvisible.
- try (var imeSession = MockImeSession.create(
- mInstrumentation.getContext(),
- mInstrumentation.getUiAutomation(),
- new ImeSettings.Builder())) {
- // Wait for any outstanding IME requests to finish, to not interfere with test.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Setup Failed: There should be no pending IME requests present when the "
- + "test starts.");
+ verifyLogging(false /* show */, ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT,
+ (imeSession, activity) -> {
+ TestUtils.runOnMainSync(() -> activity.getWindow()
+ .getDecorView()
+ .getWindowInsetsController()
+ .hide(WindowInsets.Type.ime()));
- MetricsRecorder.uploadConfigForPushedAtomWithUid(mPkgName,
- AtomsProto.Atom.IME_REQUEST_FINISHED_FIELD_NUMBER,
- false /* useUidAttributionChain */);
-
- final var activity = createTestActivity(SOFT_INPUT_STATE_UNCHANGED);
- awaitControl(WindowInsets.Type.ime(), activity);
- expectImeInvisible(TIMEOUT);
-
- TestUtils.runOnMainSync(() -> activity.getWindow()
- .getDecorView()
- .getWindowInsetsController()
- .show(WindowInsets.Type.ime()));
-
- expectImeVisible(TIMEOUT);
- // Wait for any outstanding IME requests to finish, to capture all atoms successfully.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Error: Pending IME requests took too long, likely timing out.");
-
- // Remove logs for the show requests.
- MetricsRecorder.clearReports();
-
- TestUtils.runOnMainSync(() -> activity.getWindow()
- .getDecorView()
- .getWindowInsetsController()
- .hide(WindowInsets.Type.ime()));
-
- expectImeInvisible(TIMEOUT);
- // Wait for any outstanding IME requests to finish, to capture all atoms successfully.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Error: Pending IME requests took too long, likely timing out.");
-
- final var data = MetricsRecorder.getEventMetricDataList();
- assertWithMessage("Number of atoms logged")
- .that(data.size())
- .isAtLeast(1);
-
- try {
- int successfulAtoms = 0;
- for (int i = 0; i < data.size(); i++) {
- final var atom = data.get(i).atom;
- assertThat(atom).isNotNull();
-
- final var imeRequestFinished = atom.getImeRequestFinished();
- assertThat(imeRequestFinished).isNotNull();
-
- // Skip cancelled requests.
- if (imeRequestFinished.status == ImeProtoEnums.STATUS_CANCEL) continue;
-
- successfulAtoms++;
-
- assertWithMessage("Ime Request type")
- .that(imeRequestFinished.type)
- .isEqualTo(ImeProtoEnums.TYPE_HIDE);
- assertWithMessage("Ime Request status")
- .that(imeRequestFinished.status)
- .isEqualTo(ImeProtoEnums.STATUS_SUCCESS);
- assertWithMessage("Ime Request origin")
- .that(imeRequestFinished.origin)
- .isEqualTo(ImeProtoEnums.ORIGIN_CLIENT_HIDE_SOFT_INPUT);
- }
-
- assertWithMessage("Number of successful atoms logged")
- .that(successfulAtoms)
- .isAtLeast(1);
- } catch (AssertionError e) {
- throw new AssertionError(e.getMessage() + "\natoms data:\n" + data, e);
- }
- }
+ expectImeInvisible(TIMEOUT);
+ });
}
/**
@@ -310,6 +205,38 @@
*/
@Test
public void testServerShowImeRequestFinished() throws Exception {
+ verifyLogging(true /* show */, ImeProtoEnums.ORIGIN_SERVER_START_INPUT,
+ (imeSession, activity) -> {
+ createTestActivity(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ expectImeVisible(TIMEOUT);
+ });
+ }
+
+ /**
+ * Test the logging for a server hide IME request.
+ */
+ @Test
+ public void testServerHideImeRequestFinished() throws Exception {
+ verifyLogging(false /* show */, ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT,
+ (imeSession, activity) -> {
+ // TODO: this is not actually an IME hide request from the server,
+ // but in the current configuration it is tracked like one.
+ // Will likely change in the future.
+ imeSession.callRequestHideSelf(0 /* flags */);
+
+ expectImeInvisible(TIMEOUT);
+ });
+ }
+
+ /**
+ * Verifies the logged atom events for the given test runnable and expected values.
+ *
+ * @param show whether this is testing a show request (starts with IME hidden),
+ * or hide request (starts with IME shown).
+ * @param origin the expected IME request origin.
+ * @param runnable the runnable with the test code to execute.
+ */
+ private void verifyLogging(boolean show, int origin, TestRunnable runnable) throws Exception {
// Create mockImeSession to decouple from real IMEs,
// and enable calling expectImeVisible and expectImeInvisible.
try (var imeSession = MockImeSession.create(
@@ -325,18 +252,36 @@
AtomsProto.Atom.IME_REQUEST_FINISHED_FIELD_NUMBER,
false /* useUidAttributionChain */);
- createTestActivity(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ final TestActivity activity;
+ if (show) {
+ // Use STATE_UNCHANGED to not trigger any other IME requests.
+ activity = createTestActivity(SOFT_INPUT_STATE_UNCHANGED);
+ } else {
+ // If running a hide test, start with the IME showing already.
+ activity = createTestActivity(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ expectImeVisible(TIMEOUT);
+ // Wait for any outstanding IME requests to finish, to capture all atoms.
+ PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
+ "Test Error: Pending IME requests took too long, likely timing out.");
- expectImeVisible(TIMEOUT);
- // Wait for any outstanding IME requests to finish, to capture all atoms successfully.
+ // Remove logs for the show requests.
+ MetricsRecorder.clearReports();
+ }
+
+ // Run the given test.
+ runnable.run(imeSession, activity);
+
+ // Wait for any outstanding IME requests to finish, to capture all atoms.
PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
"Test Error: Pending IME requests took too long, likely timing out.");
+ // Must have at least one atom received.
final var data = MetricsRecorder.getEventMetricDataList();
assertWithMessage("Number of atoms logged")
.that(data.size())
.isAtLeast(1);
+ // Check received atom data.
try {
int successfulAtoms = 0;
for (int i = 0; i < data.size(); i++) {
@@ -353,15 +298,16 @@
assertWithMessage("Ime Request type")
.that(imeRequestFinished.type)
- .isEqualTo(ImeProtoEnums.TYPE_SHOW);
+ .isEqualTo(show ? ImeProtoEnums.TYPE_SHOW : ImeProtoEnums.TYPE_HIDE);
assertWithMessage("Ime Request status")
.that(imeRequestFinished.status)
.isEqualTo(ImeProtoEnums.STATUS_SUCCESS);
assertWithMessage("Ime Request origin")
.that(imeRequestFinished.origin)
- .isEqualTo(ImeProtoEnums.ORIGIN_SERVER_START_INPUT);
+ .isEqualTo(origin);
}
+ // Must have at least one successful request received.
assertWithMessage("Number of successful atoms logged")
.that(successfulAtoms)
.isAtLeast(1);
@@ -371,81 +317,15 @@
}
}
- /**
- * Test the logging for a server hide IME request.
- */
- @Test
- public void testServerHideImeRequestFinished() throws Exception {
- // Create mockImeSession to decouple from real IMEs,
- // and enable calling expectImeVisible and expectImeInvisible.
- try (var imeSession = MockImeSession.create(
- mInstrumentation.getContext(),
- mInstrumentation.getUiAutomation(),
- new ImeSettings.Builder())) {
- // Wait for any outstanding IME requests to finish, to not interfere with test.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Setup Failed: There should be no pending IME requests present when the "
- + "test starts.");
+ /** Interface for the test code to be ran. */
+ private interface TestRunnable {
- MetricsRecorder.uploadConfigForPushedAtomWithUid(mPkgName,
- AtomsProto.Atom.IME_REQUEST_FINISHED_FIELD_NUMBER,
- false /* useUidAttributionChain */);
-
- createTestActivity(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- expectImeVisible(TIMEOUT);
- // Wait for any outstanding IME requests to finish, to capture all atoms successfully.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Error: Pending IME requests took too long, likely timing out.");
-
- // Remove logs for the show requests.
- MetricsRecorder.clearReports();
-
- // TODO: this is not actually an IME hide request from the server,
- // but in the current configuration it is tracked like one.
- // Will likely change in the future.
- imeSession.callRequestHideSelf(0 /* flags */);
-
- expectImeInvisible(TIMEOUT);
- // Wait for any outstanding IME requests to finish, to capture all atoms successfully.
- PollingCheck.waitFor(() -> !imeSession.hasPendingImeVisibilityRequests(),
- "Test Error: Pending IME requests took too long, likely timing out.");
-
- final var data = MetricsRecorder.getEventMetricDataList();
- assertWithMessage("Number of atoms logged")
- .that(data.size())
- .isAtLeast(1);
-
- try {
- int successfulAtoms = 0;
- for (int i = 0; i < data.size(); i++) {
- final var atom = data.get(i).atom;
- assertThat(atom).isNotNull();
-
- final var imeRequestFinished = atom.getImeRequestFinished();
- assertThat(imeRequestFinished).isNotNull();
-
- // Skip cancelled requests.
- if (imeRequestFinished.status == ImeProtoEnums.STATUS_CANCEL) continue;
-
- successfulAtoms++;
-
- assertWithMessage("Ime Request type")
- .that(imeRequestFinished.type)
- .isEqualTo(ImeProtoEnums.TYPE_HIDE);
- assertWithMessage("Ime Request status")
- .that(imeRequestFinished.status)
- .isEqualTo(ImeProtoEnums.STATUS_SUCCESS);
- assertWithMessage("Ime Request origin")
- .that(imeRequestFinished.origin)
- .isEqualTo(ImeProtoEnums.ORIGIN_SERVER_HIDE_INPUT);
- }
-
- assertWithMessage("Number of successful atoms logged")
- .that(successfulAtoms)
- .isAtLeast(1);
- } catch (AssertionError e) {
- throw new AssertionError(e.getMessage() + "\natoms data:\n" + data, e);
- }
- }
+ /**
+ * Execute the given test code given the ime session and activity.
+ *
+ * @param imeSession the initialized mock ime session.
+ * @param activity the initialized test activity.
+ */
+ void run(@NonNull MockImeSession imeSession, @NonNull TestActivity activity);
}
}