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

Commit 7a69759a authored by David Tseng's avatar David Tseng
Browse files

Fix and improve RequestFocusTest

RequestFocusTest used the deprecated ActivityInstrumentationTestCase2
and associated support. It also uses the @UiThreadTest annotation which
is only supported in junit4 leading to half of the suite failing with
exceptions due to calls on a non-main thread.

This change:
- migrates to junit4
- uses modern constructs like ActivityScenarioRule
- moves UI thread dependent logic to ActivityScenarioRule.onActivity
- resolves new base assumption of default touch mode by adding logic to force non-touch by sending a key event
- rebaselines behaviors e.g. global focus change is no longer called
- motivates more tests for keyboard + focus
- will be used to also adapt this suite for system + a11y focus testing

Bug: 393668723
Test: atest RequestFocusTest
Change-Id: Ifcdb1c5a5bb7508ed94ee4799f1412d13bab6086
parent 69565e5b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ public class RequestFocus extends Activity {

        // bottom right button starts with the focus.
        final Button bottomRightButton = findViewById(R.id.bottomRightButton);
        bottomRightButton.setFocusableInTouchMode(true);
        bottomRightButton.requestFocus();
        bottomRightButton.setText("I should have focus");
    }
+111 −101
Original line number Diff line number Diff line
@@ -16,71 +16,79 @@

package android.widget.focus;

import static org.junit.Assert.*;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;

import android.os.Handler;
import android.test.ActivityInstrumentationTestCase2;
import android.util.AndroidRuntimeException;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import android.widget.Button;

import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.frameworks.coretests.R;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;

/**
 * {@link RequestFocusTest} is set up to exercise cases where the views that
 * have focus become invisible or GONE.
 */
public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {

@RunWith(AndroidJUnit4.class)
public class RequestFocusTest {
    private Button mTopLeftButton;
    private Button mBottomLeftButton;
    private Button mTopRightButton;
    private Button mBottomRightButton;
    private Handler mHandler;

    public RequestFocusTest() {
        super(RequestFocus.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
    @Rule
    public ActivityScenarioRule<RequestFocus> activityRule =
            new ActivityScenarioRule<>(RequestFocus.class);

        final RequestFocus a = getActivity();
    @Before
    public void setViewFieldsAndForceNonTouchMode() throws Exception {
        activityRule.getScenario().onActivity(a -> {
            mHandler = a.getHandler();
            mTopLeftButton = (Button) a.findViewById(R.id.topLeftButton);
            mBottomLeftButton = (Button) a.findViewById(R.id.bottomLeftButton);
            mTopRightButton = (Button) a.findViewById(R.id.topRightButton);
            mBottomRightButton = (Button) a.findViewById(R.id.bottomRightButton);
    }

    // Test that setUp did what we expect it to do.  These asserts
    // can't go in SetUp, or the test will hang.
    @MediumTest
    public void testSetUpConditions() throws Exception {
            assertNotNull(mHandler);
            assertNotNull(mTopLeftButton);
            assertNotNull(mTopRightButton);
            assertNotNull(mBottomLeftButton);
            assertNotNull(mBottomRightButton);
            assertTrue("requestFocus() should work from onCreate.", mBottomRightButton.hasFocus());
        });

        // Force non-touch mode by sending a key.
        android.app.Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
        instrumentation.waitForIdleSync(); // Wait for UI thread to be idle
    }

    // Test that a posted requestFocus works.
    @Test
    @LargeTest
    public void testPostedRequestFocus() throws Exception {
        mHandler.post(new Runnable() { public void run() {
        mHandler.post(new Runnable() {
            public void run() {
                mBottomLeftButton.requestFocus();
        }});
            }
        });
        synchronized (this) {
            try {
                wait(500);
@@ -92,6 +100,7 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo
    }

    // Test that a requestFocus from the wrong thread fails.
    @Test
    @MediumTest
    public void testWrongThreadRequestFocusFails() throws Exception {
        try {
@@ -112,30 +121,30 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo
     *
     * @throws Exception If an error occurs.
     */
    @UiThreadTest
    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable()
            throws Exception {
    @Test
    @LargeTest
    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable() throws Exception {
        activityRule.getScenario().onActivity(a -> {
            // Get the first focusable.
            Button clearingFocusButton = mTopLeftButton;
            Button gainingFocusButton = mTopLeftButton;

            // Make sure that the clearing focus View is the first focusable.
        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
                View.FOCUS_FORWARD);
        assertSame("The clearing focus button is the first focusable.",
                clearingFocusButton, focusCandidate);
        assertSame("The gaining focus button is the first focusable.",
                gainingFocusButton, focusCandidate);
            View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(
                    null, View.FOCUS_FORWARD);
            assertSame("The clearing focus button is the first focusable.", clearingFocusButton,
                    focusCandidate);
            assertSame("The gaining focus button is the first focusable.", gainingFocusButton,
                    focusCandidate);

            // Focus the clearing focus button.
            clearingFocusButton.requestFocus();
            assertTrue(clearingFocusButton.hasFocus());

            // Register the invocation order checker.
        CombinedListeners mock = mock(CombinedListeners.class);
            OnFocusChangeListener mock = mock(OnFocusChangeListener.class);
            clearingFocusButton.setOnFocusChangeListener(mock);
            gainingFocusButton.setOnFocusChangeListener(mock);
        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);

            // Try to clear focus.
            clearingFocusButton.clearFocus();
@@ -143,12 +152,10 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo
            // Check that no callback was invoked since focus did not move.
            InOrder inOrder = inOrder(mock);
            inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
        inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
            inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
        });
    }

    public interface CombinedListeners extends OnFocusChangeListener, OnGlobalFocusChangeListener {}

    /**
     * This tests check whether the on focus change callbacks are invoked in
     * the proper order when a View loses focus and the framework gives it to
@@ -156,29 +163,31 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo
     *
     * @throws Exception
     */
    @UiThreadTest
    @Test
    @LargeTest
    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfNotFirstFocusable()
            throws Exception {
        activityRule.getScenario().onActivity(a -> {
            Button clearingFocusButton = mTopRightButton;
            Button gainingFocusButton = mTopLeftButton;

            // Make sure that the clearing focus View is not the first focusable.
        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
                View.FOCUS_FORWARD);
            View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(
                    null, View.FOCUS_FORWARD);

            assertNotSame("The clearing focus button is not the first focusable.",
                    clearingFocusButton, focusCandidate);
        assertSame("The gaining focus button is the first focusable.",
                gainingFocusButton, focusCandidate);
            assertSame("The gaining focus button is the first focusable.", gainingFocusButton,
                    focusCandidate);

            // Focus the clearing focus button.
            clearingFocusButton.requestFocus();
            assertTrue(clearingFocusButton.hasFocus());

            // Register the invocation order checker.
        CombinedListeners mock = mock(CombinedListeners.class);
            OnFocusChangeListener mock = mock(OnFocusChangeListener.class);
            clearingFocusButton.setOnFocusChangeListener(mock);
            gainingFocusButton.setOnFocusChangeListener(mock);
        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);

            // Try to clear focus.
            clearingFocusButton.clearFocus();
@@ -186,7 +195,8 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo
            // Check that no callback was invoked since focus did not move.
            InOrder inOrder = inOrder(mock);
            inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
        inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);

            inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
        });
    }
}