facebook / screenshot-tests-for-android

Generate fast deterministic screenshots during Android instrumentation tests
http://facebook.github.io/screenshot-tests-for-android
Apache License 2.0
1.74k stars 229 forks source link

Crash when creating accessibility info node #248

Closed pedrovgs closed 4 years ago

pedrovgs commented 4 years ago

Bug description: I'm using this library in a medium-size project and I'm facing some issues related to the accessibility info node generated by AccessibilityUtil.createNodeInfoFromView. Looks like this method is being used out of the main thread of the testing host application and this is causing exceptions like this:

04-23 16:19:48.628 E/Error    ( 2799): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
04-23 16:19:48.628 E/Error    ( 2799):  at android.os.Handler.<init>(Handler.java:203)
04-23 16:19:48.628 E/Error    ( 2799):  at android.os.Handler.<init>(Handler.java:117)
04-23 16:19:48.628 E/Error    ( 2799):  at android.content.ClipboardManager$2.<init>(ClipboardManager.java:62)
04-23 16:19:48.628 E/Error    ( 2799):  at android.content.ClipboardManager.<init>(ClipboardManager.java:62)
04-23 16:19:48.628 E/Error    ( 2799):  at android.app.SystemServiceRegistry$11.createService(SystemServiceRegistry.java:249)
04-23 16:19:48.628 E/Error    ( 2799):  at android.app.SystemServiceRegistry$11.createService(SystemServiceRegistry.java:247)
04-23 16:19:48.628 E/Error    ( 2799):  at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:928)
04-23 16:19:48.628 E/Error    ( 2799):  at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:880)
04-23 16:19:48.628 E/Error    ( 2799):  at android.app.ContextImpl.getSystemService(ContextImpl.java:1651)
04-23 16:19:48.628 E/Error    ( 2799):  at android.content.ContextWrapper.getSystemService(ContextWrapper.java:708)
04-23 16:19:48.628 E/Error    ( 2799):  at io.github.inflationx.viewpump.ViewPumpContextWrapper.getSystemService(ViewPumpContextWrapper.java:92)
04-23 16:19:48.628 E/Error    ( 2799):  at android.view.ContextThemeWrapper.getSystemService(ContextThemeWrapper.java:171)
04-23 16:19:48.628 E/Error    ( 2799):  at android.app.Activity.getSystemService(Activity.java:5893)
04-23 16:19:48.628 E/Error    ( 2799):  at android.widget.TextView.canPaste(TextView.java:11104)
04-23 16:19:48.628 E/Error    ( 2799):  at android.widget.TextView.onInitializeAccessibilityNodeInfoInternal(TextView.java:10394)
04-23 16:19:48.628 E/Error    ( 2799):  at android.widget.EditText.onInitializeAccessibilityNodeInfoInternal(EditText.java:177)
04-23 16:19:48.628 E/Error    ( 2799):  at android.view.View.onInitializeAccessibilityNodeInfo(View.java:7307)
04-23 16:19:48.628 E/Error    ( 2799):  at androidx.core.view.ViewCompat.onInitializeAccessibilityNodeInfo(ViewCompat.java:656)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.createNodeInfoFromView(AccessibilityUtil.java:179)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.createNodeInfoFromView(AccessibilityUtil.java:156)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.access$000(AccessibilityUtil.java:41)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil$AXTreeNode.<init>(AccessibilityUtil.java:601)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:580)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.layouthierarchy.AccessibilityUtil.generateAccessibilityTree(AccessibilityUtil.java:585)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.internal.ScreenshotImpl.record(ScreenshotImpl.java:277)
04-23 16:19:48.628 E/Error    ( 2799):  at com.facebook.testing.screenshot.internal.RecordBuilderImpl.record(RecordBuilderImpl.java:153)

The exception seems to be triggered deep down in the library, close to the accessibility node information extraction process. However, if you go up in the stack trace you'll see how the most relevant point is this. Inside this try/catch block, the exception is being thrown again but the screenshot is already saved so looks like this information would be optional and not mandatory.

If we look at the code handling accessibility information looks like we should move the code to the target application UI thread. I've seen there are some comments and try/catch events related to different exceptions and errors the author is not able to fully control. I'm wondering if moving this code to the UI thread would fix the issue.

Library version: I've found the issue while using version 0.12.0 but I think this bug was introduced in 0.9.0. OS version where I've reproduced the error: Any Emulator API 26 Repository reproducing the error:

I'm afraid I've found this error in a project I'm working on and I can't share the code with any other developer because of the obvious NDA. The project has around 200 screenshot tests with some of them taking screenshots of activities and others only views, view holders, or dialogs.

Current workaround:

As a workaround, I tried to capture the exception so my tests can finish the execution and the gradle plugin can compare or save the screenshots...but this is not always working and it's generating other executions in some of my tests related to accessibility issues I don't actually understand. This workaround is not ideal at all, any other error would be making screenshot not to be taken and we would have no idea so I think we should fix the bug. Do you know if we could find any workaround in order to get it working before releasing the fix?

untalfranfernandez commented 4 years ago

I'm glad I'm not the only one suffering this issue, I thought I was related to how I'm using the library, but it seems to be a deep issue within this tool. I hope it gets fixed soon so I can reactivate the "unstable" tests.

pedrovgs commented 4 years ago

I'm looking debugging the sample app and testing some changes and it seems the error is not related to where thread use. The code throwing the exception is out of the scope of this repository and there is no new stable version we could try to use or review if fixes the error.

The support library implementing this method invocation throws the already reported RuntimeException:

ViewCompat.onInitializeAccessibilityNodeInfo(view, nodeInfo);

The current implementation was expecting an IndexOutOfBound exception but not the RuntimeException I've found. As there is a comment and a retry mechanism implemented around the first mentioned exception I'm going to send a PR with a small code modification to see if we can use it to avoid the error until the support library team fixes the issue.

pedrovgs commented 4 years ago

Ok this is the tentative fix I've found. I'm going to sign the CLA and I'll wait for the library author review 😃 Last but not least, I'd like to thank all the library collaborators for the awesome project they've created. We use it in every project and right now is one of the best tools we have when writing automated tests.

xiphirx commented 4 years ago

Tagging @blavalla for visibility.

pedrovgs commented 4 years ago

Hi @blavalla @xiphirx today I've been reviewing the whole test suit in order to see if there is more information I can provide to solve this issue and I've found something interesting. The accessibility error is only reproducible when there are screenshot tests for dialogs. I don't know why, the ViewCompat class throws the reported exception when making a screenshot of any instance extending from android.app.Dialog. I think this feature is quite common and we should pay attention to exceptions like these because previous versions of the library supported this scenario. Since 0.9.0, when the accessibility feature was introduced, this started to fail. Keep in mind the exceptions thrown stops the whole test suite execution and this is really painful.

After some checks I've noticed the fix I've sent in PR #249 solves most of the scenarios where the tool is failing but I don't understand why sometimes keeps failing with an IndexOutOfBoundException like this:

04-23 15:07:07.622 E/AndroidRuntime(21467): java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
04-23 15:07:07.622 E/AndroidRuntime(21467):     at java.util.ArrayList.get(ArrayList.java:437)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.ViewGroup.onInitializeAccessibilityNodeInfoInternal(ViewGroup.java:3551)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.onInitializeAccessibilityNodeInfo(View.java:7307)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.createAccessibilityNodeInfoInternal(View.java:7266)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.createAccessibilityNodeInfo(View.java:7251)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.accessibility.AccessibilityRecord.setSource(AccessibilityRecord.java:146)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.accessibility.AccessibilityRecord.setSource(AccessibilityRecord.java:119)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.onInitializeAccessibilityEventInternal(View.java:7203)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.onInitializeAccessibilityEvent(View.java:7191)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:7053)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.View.sendAccessibilityEventUnchecked(View.java:7038)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.view.ViewRootImpl$SendWindowContentChangedAccessibilityEvent.run(ViewRootImpl.java:7891)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.os.Handler.handleCallback(Handler.java:789)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.os.Handler.dispatchMessage(Handler.java:98)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.os.Looper.loop(Looper.java:164)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at android.app.ActivityThread.main(ActivityThread.java:6541)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at java.lang.reflect.Method.invoke(Native Method)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
04-23 15:07:07.622 E/AndroidRuntime(21467):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

Something inside the view instance or the ViewCompat library is scheduling an accessibility evaluation using a handler outside the testing application thread and this is blowing the stack and consequently stopping test execution. This means the fix I've sent in #249 fix some scenarios but is not enough. That's why I'm preparing another PR to disable the usage of the accessibility information report so we can avoid the error when needed by disabling this feature. This is not ideal, but will let us use this library in different scenarios even if ViewCompat utility methods are failing.

pedrovgs commented 4 years ago

The new PR making the accessibility feature optional is ready #250 😃 Please @xiphirx @blavalla review it if you have time. Thanks for your time and patience maintaining and creating this library. I hope we can find a solution and publish it soon 😃

pedrovgs commented 4 years ago

Hi @xiphirx could you please release a new version now this issue has been fixed?

xiphirx commented 4 years ago

New version should be in maven central shortly.