ahsing / android-test-kit

Automatically exported from code.google.com/p/android-test-kit
0 stars 0 forks source link

ViewAction closeSoftKeyboard() does ensure that soft keyboard is completely gone #79

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Soft keyboard enabled
2. EditText with focus to bring up the soft keyboard
3. Button that would normally get blocked by the soft keyboard
4. In an Espresso test, click on the button immediately after performing the 
closeSoftKeyboard() ViewAction.

What is the expected output?
The soft keyboard gets dismissed and the click on the button is successful.

What do you see instead?
On certain devices (physical Nexus 4 with API19 and Nexus 5 emulator with 
API19), the click is invoked while the soft keyboard is animating away despite 
disabling animations in the Developer Settings. If the click is done on the 
soft keyboard, a SecurityException will be thrown.

What version of the product are you using? On what operating system?
Espresso v1.1
Physical Nexus 4 Android 4.4.2 (API19)
Emulator Nexus 5 Android 4.4.2 (API19)

Please provide any additional information below.
The issue appears to be mitigated if I add a sleep of 500 milliseconds.
Please see: 
https://groups.google.com/forum/#!searchin/android-test-kit-discuss/keyboard/and
roid-test-kit-discuss/IX-D3BRWQsE/-XAQmW9HrRMJ

Here is a sample call stack from one of our tests that ran into this issue:
com.google.android.apps.common.testing.ui.espresso.PerformException: Error 
performing 'single click' on view 'with id: is <2131296505>'.
    at com.google.android.apps.common.testing.ui.espresso.PerformException$Builder.build(PerformException.java:67)
    at com.google.android.apps.common.testing.ui.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:57)
    at com.google.android.apps.common.testing.ui.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:40)
    at com.google.android.apps.common.testing.ui.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:159)
    at com.google.android.apps.common.testing.ui.espresso.ViewInteraction.doPerform(ViewInteraction.java:90)
    at com.google.android.apps.common.testing.ui.espresso.ViewInteraction.perform(ViewInteraction.java:73)
    at com.twitter.android.coverage.TextFirstComposerActivityTest.postActivityWithASimpleImageTweetHelper(TextFirstComposerActivityTest.java:219)
    at com.twitter.android.coverage.TextFirstComposerActivityTest.testPostActivityWithASimpleImageTweetInLandscape(TextFirstComposerActivityTest.java:96)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
    at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
    at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
    at com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner.onStart(GoogleInstrumentationTestRunner.java:167)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
Caused by: com.google.android.apps.common.testing.ui.espresso.PerformException: 
Error performing 'Send down montion event' on view 'unknown'.
    at com.google.android.apps.common.testing.ui.espresso.PerformException$Builder.build(PerformException.java:67)
    at com.google.android.apps.common.testing.ui.espresso.action.MotionEvents.sendDown(MotionEvents.java:90)
    at com.google.android.apps.common.testing.ui.espresso.action.Tap$1.sendTap(Tap.java:24)
    at com.google.android.apps.common.testing.ui.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:82)
    at com.google.android.apps.common.testing.ui.espresso.ViewInteraction$1.run(ViewInteraction.java:119)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5017)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: 
com.google.android.apps.common.testing.ui.espresso.InjectEventSecurityException:
 java.lang.SecurityException: Injecting to another application requires 
INJECT_EVENTS permission
    at com.google.android.apps.common.testing.ui.espresso.base.InputManagerEventInjectionStrategy.injectMotionEvent(InputManagerEventInjectionStrategy.java:131)
    at com.google.android.apps.common.testing.ui.espresso.base.EventInjector.injectMotionEvent(EventInjector.java:80)
    at com.google.android.apps.common.testing.ui.espresso.base.UiControllerImpl$3.call(UiControllerImpl.java:201)
    at com.google.android.apps.common.testing.ui.espresso.base.UiControllerImpl$3.call(UiControllerImpl.java:198)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
    at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.SecurityException: Injecting to another application 
requires INJECT_EVENTS permission
    at android.os.Parcel.readException(Parcel.java:1465)
    at android.os.Parcel.readException(Parcel.java:1419)
    at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:356)
    at android.hardware.input.InputManager.injectInputEvent(InputManager.java:641)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at com.google.android.apps.common.testing.ui.espresso.base.InputManagerEventInjectionStrategy.injectMotionEvent(InputManagerEventInjectionStrategy.java:122)
    ... 9 more

Original issue reported on code.google.com by mhernan...@gmail.com on 3 Jun 2014 at 7:15

GoogleCodeExporter commented 9 years ago
Is it a consistent repro? (we rarely test on physical devices, but I can try 
this out if it's easy to reproduce).

Original comment by vale...@google.com on 9 Jun 2014 at 9:59

GoogleCodeExporter commented 9 years ago
As mentioned above, I am also able to reproduce this on an emulator. It is not 
100% consistent, however it is consistent enough that I had to add a 500 
millisecond sleep after every call to closeSoftKeyboard() in order to make my 
tests reliable.

If you follow the thread in 
https://groups.google.com/forum/#!searchin/android-test-kit-discuss/keyboard/and
roid-test-kit-discuss/IX-D3BRWQsE/-XAQmW9HrRMJ, R Palan mentions that he 
specifically had to configure the emulator to "hardware keyboard present" as a 
workaround, meaning that he was also able to reproduce this on an emulator.

Original comment by mhernan...@gmail.com on 10 Jun 2014 at 4:22

GoogleCodeExporter commented 9 years ago
I find that on older devices, like the Samsung SIII Mini, this happens quite 
regularly. I'd say a given test has a 50% chance of failing.

Original comment by stern....@gmail.com on 21 Jul 2014 at 10:11

GoogleCodeExporter commented 9 years ago
I found the best work around is to use @FlakyTest(tolerance = 5), which will 
tell the instrumentation test runner to try a x times before marking the test 
as failed.

Original comment by stern....@gmail.com on 21 Jul 2014 at 10:36

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Same problem here. Need to wait for the keyboard to close. Just because the RPC 
call returned fast the UI is still blocked by the keyboard.

Sleeping seems the ugliest but the only solution that works.

Original comment by dnkoutso@gmail.com on 24 Jul 2014 at 6:46

GoogleCodeExporter commented 9 years ago
In the meantime, I wound up implementing my own wrapper closeSoftKeyboard() 
ViewAction like so:

public static ViewAction closeSoftKeyboard() {
    return new ViewAction() {
        /**
         * The delay time to allow the soft keyboard to dismiss.
         */
        private static final long KEYBOARD_DISMISSAL_DELAY_MILLIS = 1000L;

        /**
         * The real {@link CloseKeyboardAction} instance.
         */
        private final ViewAction mCloseSoftKeyboard = new CloseKeyboardAction();

        @Override
        public Matcher<View> getConstraints() {
            return mCloseSoftKeyboard.getConstraints();
        }

        @Override
        public String getDescription() {
            return mCloseSoftKeyboard.getDescription();
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            mCloseSoftKeyboard.perform(uiController, view);
            uiController.loopMainThreadForAtLeast(KEYBOARD_DISMISSAL_DELAY_MILLIS);
        }
    };
}

Original comment by mhernan...@gmail.com on 24 Jul 2014 at 8:11

GoogleCodeExporter commented 9 years ago
I worked around this problem by implementing my own keyboard that is only 32dp 
tall that I use on testing devices.

https://github.com/johnybot/TestingKeyboard

Original comment by johnatha...@hootsuite.com on 28 Jul 2014 at 8:59

GoogleCodeExporter commented 9 years ago
Is this something that happens even when system animations are turned off?

Original comment by vale...@google.com on 19 Nov 2014 at 9:39

GoogleCodeExporter commented 9 years ago
Yes. I was running into this with all three system animations off.

Original comment by mhernan...@gmail.com on 21 Nov 2014 at 7:22

GoogleCodeExporter commented 9 years ago
I also was running into this with animations turned off.

Original comment by stern....@gmail.com on 21 Nov 2014 at 3:09

GoogleCodeExporter commented 9 years ago
I didn't use Espresso 1.1. I tried Espresso 2.0 and I'm running into this with 
all three system animations off (I think keyboard dismiss animation is not 
affected by these options).

The bug can be reproduced using your own tests (lanscape mode), Google Keyboard 
and:
Nexus7 (2012), Android 5.0.2
Nexus5 - Android 5.0.1

I opened an issue there:
https://github.com/googlesamples/android-testing/issues/5

It's not an invalid issue, otherwise Espresso 2.0 does not support real devices.
https://code.google.com/p/android-test-kit/issues/detail?id=44

I understand that the idea is to use emulators, CI servers and cloud but I 
thought your test kit supports real devices to confirm results.

KEYBOARD_DISMISSAL_DELAY works to slve the issue and demostrate it.

Original comment by ard...@gmail.com on 28 Dec 2014 at 6:50

Attachments:

GoogleCodeExporter commented 9 years ago
I'd forgotten about this issue. I haven't upgraded to 2.0 yet, but for 1.1, I 
put together a custom ViewAction to close the softkeyboard that synchronized on 
a somewhat shady detection of the keyboard being removed. I haven't had time to 
clean it up or test it in 2.0, but if it'll help someone in the meantime or if 
someone can add some proper testing for it, I've attached my source class. 
There were still occasional failures that I never had a chance to track down, 
but it occurred so infrequently that it didn't affect my testing overall. It 
may not be production ready per se.

Original comment by p.pr...@blueesoteric.com on 28 Dec 2014 at 7:32

Attachments: