Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd4ea64e authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Android (Google) Code Review
Browse files

Merge "Add autoshow testcase."

parents ea975a77 454d7615
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -19,7 +19,8 @@
    package="com.android.inputmethod.stresstest">

    <application>
        <activity android:name=".TestActivity"/>
        <activity android:name=".AutoShowTest$TestActivity"/>
        <activity android:name=".ImeOpenCloseStressTest$TestActivity"/>
    </application>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+83 −0
Original line number Diff line number Diff line
/*
 * 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 static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;

import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.RootPermissionTest;
import android.widget.EditText;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Test;
import org.junit.runner.RunWith;

@RootPermissionTest
@RunWith(AndroidJUnit4.class)
public final class AutoShowTest {

    @Test
    public void autoShow() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent = new Intent()
                .setAction(Intent.ACTION_MAIN)
                .setClass(instrumentation.getContext(), TestActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        TestActivity activity = (TestActivity) instrumentation.startActivitySync(intent);
        EditText editText = activity.getEditText();
        waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
        waitOnMainUntilImeIsShown(editText);
    }

    public static class TestActivity extends Activity {
        private EditText mEditText;

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // IME will be auto-shown if the following conditions are met:
            // 1. SoftInputMode state is SOFT_INPUT_STATE_UNSPECIFIED.
            // 2. SoftInputMode adjust is SOFT_INPUT_ADJUST_RESIZE.
            getWindow().setSoftInputMode(SOFT_INPUT_STATE_UNSPECIFIED | SOFT_INPUT_ADJUST_RESIZE);
            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);
            // 3. The focused view is a text editor (View#onCheckIsTextEditor() returns true).
            mEditText.requestFocus();
        }

        public EditText getEditText() {
            return mEditText;
        }
    }
}
+48 −31
Original line number Diff line number Diff line
@@ -16,64 +16,81 @@

package com.android.inputmethod.stresstest;

import static com.android.compatibility.common.util.SystemUtil.eventually;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import static com.google.common.truth.Truth.assertThat;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.RootPermissionTest;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

@RootPermissionTest
@RunWith(AndroidJUnit4.class)
public class ImeOpenCloseStressTest {
public final class ImeOpenCloseStressTest {

    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
    private static final int NUM_TEST_ITERATIONS = 100;

    private Instrumentation mInstrumentation;

    @Test
    public void test() {
        mInstrumentation = InstrumentationRegistry.getInstrumentation();
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent = new Intent()
                .setAction(Intent.ACTION_MAIN)
                .setClass(mInstrumentation.getContext(), TestActivity.class)
                .setClass(instrumentation.getContext(), TestActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        TestActivity activity = (TestActivity) mInstrumentation.startActivitySync(intent);
        eventually(() -> 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> V callOnMainSync(Callable<V> callable) {
        AtomicReference<V> result = new AtomicReference<>();
        AtomicReference<Exception> thrownException = new AtomicReference<>();
        mInstrumentation.runOnMainSync(() -> {
            try {
                result.set(callable.call());
            } catch (Exception e) {
                thrownException.set(e);
    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);
        }
        });
        if (thrownException.get() != null) {
            throw new RuntimeException("Exception thrown from Main thread", thrownException.get());

        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();
    }
}
+80 −0
Original line number Diff line number Diff line
/*
 * 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> V callOnMainSync(Callable<V> callable) {
        AtomicReference<V> 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.
     *
     * <p>The given {@code pred} will be called on the main thread.
     */
    public static void waitOnMainUntil(String message, Callable<Boolean> 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);
    }
}
+0 −64
Original line number Diff line number Diff line
/*
 * 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);
    }
}