diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml index e5d65188f7ad..f5fe8f2e8e76 100644 --- a/tests/InputMethodStressTest/AndroidManifest.xml +++ b/tests/InputMethodStressTest/AndroidManifest.xml @@ -19,7 +19,8 @@ package="com.android.inputmethod.stresstest"> - + + assertThat(callOnMainSync(activity::hasWindowFocus)).isTrue(), TIMEOUT); + TestActivity activity = (TestActivity) instrumentation.startActivitySync(intent); + EditText editText = activity.getEditText(); + waitOnMainUntil("activity should gain focus", editText::hasWindowFocus); for (int i = 0; i < NUM_TEST_ITERATIONS; i++) { - mInstrumentation.runOnMainSync(activity::showIme); - eventually(() -> assertThat(callOnMainSync(activity::isImeShown)).isTrue(), TIMEOUT); - mInstrumentation.runOnMainSync(activity::hideIme); - eventually(() -> assertThat(callOnMainSync(activity::isImeShown)).isFalse(), TIMEOUT); + instrumentation.runOnMainSync(activity::showIme); + waitOnMainUntilImeIsShown(editText); + instrumentation.runOnMainSync(activity::hideIme); + waitOnMainUntilImeIsHidden(editText); } } - private V callOnMainSync(Callable callable) { - AtomicReference result = new AtomicReference<>(); - AtomicReference thrownException = new AtomicReference<>(); - mInstrumentation.runOnMainSync(() -> { - try { - result.set(callable.call()); - } catch (Exception e) { - thrownException.set(e); - } - }); - if (thrownException.get() != null) { - throw new RuntimeException("Exception thrown from Main thread", thrownException.get()); + public static class TestActivity extends Activity { + + private EditText mEditText; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LinearLayout rootView = new LinearLayout(this); + rootView.setOrientation(LinearLayout.VERTICAL); + mEditText = new EditText(this); + rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + setContentView(rootView); + } + + public EditText getEditText() { + return mEditText; + } + + public void showIme() { + mEditText.requestFocus(); + InputMethodManager imm = getSystemService(InputMethodManager.class); + imm.showSoftInput(mEditText, 0); + } + + public void hideIme() { + InputMethodManager imm = getSystemService(InputMethodManager.class); + imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); } - return result.get(); } } diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java new file mode 100644 index 000000000000..ba2ba3c75bc2 --- /dev/null +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 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.inputmethod.stresstest; + +import static com.android.compatibility.common.util.SystemUtil.eventually; + +import static com.google.common.truth.Truth.assertWithMessage; + +import android.view.View; +import android.view.WindowInsets; + +import androidx.test.platform.app.InstrumentationRegistry; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +/** Utility methods for IME stress test. */ +public final class ImeStressTestUtil { + + private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); + + private ImeStressTestUtil() { + } + + /** Checks if the IME is shown on the window that the given view belongs to. */ + public static boolean isImeShown(View view) { + WindowInsets insets = view.getRootWindowInsets(); + return insets.isVisible(WindowInsets.Type.ime()); + } + + /** Calls the callable on the main thread and returns the result. */ + public static V callOnMainSync(Callable callable) { + AtomicReference result = new AtomicReference<>(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + try { + result.set(callable.call()); + } catch (Exception e) { + throw new RuntimeException("Exception was thrown", e); + } + }); + return result.get(); + } + + /** + * Waits until {@code pred} returns true, or throws on timeout. + * + *

The given {@code pred} will be called on the main thread. + */ + public static void waitOnMainUntil(String message, Callable pred) { + eventually(() -> assertWithMessage(message).that(pred.call()).isTrue(), TIMEOUT); + } + + /** Waits until IME is shown, or throws on timeout. */ + public static void waitOnMainUntilImeIsShown(View view) { + eventually(() -> assertWithMessage("IME should be shown").that( + callOnMainSync(() -> isImeShown(view))).isTrue(), TIMEOUT); + } + + /** Waits until IME is hidden, or throws on timeout. */ + public static void waitOnMainUntilImeIsHidden(View view) { + //eventually(() -> assertThat(callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT); + eventually(() -> assertWithMessage("IME should be hidden").that( + callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT); + } +} diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/TestActivity.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/TestActivity.java deleted file mode 100644 index 7baf037e4215..000000000000 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/TestActivity.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2021 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.inputmethod.stresstest; - -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; - -import android.app.Activity; -import android.os.Bundle; -import android.view.WindowInsets; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.LinearLayout; - -import androidx.annotation.Nullable; - -public class TestActivity extends Activity { - - private EditText mEditText; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - LinearLayout rootView = new LinearLayout(this); - rootView.setOrientation(LinearLayout.VERTICAL); - mEditText = new EditText(this); - rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); - setContentView(rootView); - } - - public boolean hasWindowFocus() { - return mEditText.hasWindowFocus(); - } - - public boolean isImeShown() { - WindowInsets insets = mEditText.getRootWindowInsets(); - return insets.isVisible(WindowInsets.Type.ime()); - } - - public void showIme() { - mEditText.requestFocus(); - InputMethodManager imm = getSystemService(InputMethodManager.class); - imm.showSoftInput(mEditText, 0); - } - - public void hideIme() { - InputMethodManager imm = getSystemService(InputMethodManager.class); - imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); - } -}