Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Allow Maestro to work on Android API levels 25 and lower (Android 7.1.1 and lower) #1527

Conversation

testifyqa
Copy link
Contributor

@testifyqa testifyqa commented Oct 14, 2023

Sorry accidentally deleted this,

This allows Maestro to work for Android API 24 and above

@NyCodeGHG
Copy link
Contributor

I tried to run this on an API 25 virtual device and the advanced flow fails on the second step with the following exception

Running on emulator-5554                                                      
                                                                              
 ║                                                                            
 ║  > Flow                                                                    
 ║                                                                            
 ║    ⏳  Run subflows/onboarding-android.yaml                                 
 ║      ✅  Launch app "org.wikipedia"                                         
 ║      ⏳  Tap on id: org.wikipedia:id/fragment_onboarding_forward_button     
 ║      🔲 Tap on id: org.wikipedia:id/fragment_onboarding_forward_button     
 ║      🔲 Tap on id: org.wikipedia:id/fragment_onboarding_forward_button     
 ║      🔲 Tap on id: org.wikipedia:id/fragment_onboarding_done_button        
 ║    🔲 Tap on id: org.wikipedia:id/search_container                         
 ║    🔲 Run scripts/getSearchQuery.js                                        
 ║    🔲 Input text ${output.result}
 ║    🔲 Assert that "${output.result}" is visible
 ║
Exception in thread "pool-4-thread-1" java.lang.Error: java.io.IOException: Command failed (tcp:7001): closed
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1155)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:750)
Caused by: java.io.IOException: Command failed (tcp:7001): closed
        at dadb.adbserver.AdbServer.send$dadb(AdbServer.kt:99)
        at dadb.adbserver.AdbServerDadb.open(AdbServer.kt:138)
        at dadb.forwarding.TcpForwarder.handleForwarding$lambda-1(TcpForwarder.kt:64)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        ... 2 more

@testifyqa
Copy link
Contributor Author

Thanks for checking this, I admit I only tested this with the simple flow.

I will try to find some more time to dig into this.

@testifyqa
Copy link
Contributor Author

@NyCodeGHG I've made an additional tweak. It seems AccessibilityNodeInfo hintText is not in API 25 and lower.

I don't have my API 25 device on hand at the moment, so if you get a chance it would be great if you could test again with the advanced flow. Many thanks.

@testifyqa testifyqa force-pushed the fix/allow-maestro-to-work-on-android-api-level-25-and-lower branch 2 times, most recently from d3f4e1b to 116a96d Compare October 20, 2023 17:24
@NyCodeGHG
Copy link
Contributor

NyCodeGHG commented Oct 23, 2023

Now it just crashes on API level 25, not even launching the app

Unable to launch app org.wikipedia: UNAVAILABLE: io exception

edit: seems like you accidentally reverted your changes?
after redoing these changes, it still crashes on the second step

@testifyqa
Copy link
Contributor Author

Thanks, I did accidentally revert my previous changes.

Can I ask how you are building? In Logcat I see this exception...

2023-10-23 14:24:51.221 32687-663   AndroidRuntime          dev.mobile.maestro                   E  FATAL EXCEPTION: grpc-default-executor-0
                                                                                                    Process: dev.mobile.maestro, PID: 32687
                                                                                                    java.lang.NoSuchMethodError: No virtual method getHintText()Ljava/lang/CharSequence; in class Landroid/view/accessibility/AccessibilityNodeInfo; or its super classes (declaration of 'android.view.accessibility.AccessibilityNodeInfo' appears in /system/framework/framework.jar:classes2.dex)
                                                                                                    	at dev.mobile.maestro.ViewHierarchy.dumpNodeRec(ViewHierarchy.kt:123)
                                                                                                    	at dev.mobile.maestro.ViewHierarchy.dumpNodeRec$default(ViewHierarchy.kt:111)
                                                                                                    	at dev.mobile.maestro.ViewHierarchy.dump(ViewHierarchy.kt:71)
                                                                                                    	at dev.mobile.maestro.ViewHierarchy.dump$default(ViewHierarchy.kt:27)
                                                                                                    	at dev.mobile.maestro.Service.viewHierarchy(MaestroDriverService.kt:167)
                                                                                                    	at maestro_android.MaestroDriverGrpc$MethodHandlers.invoke(MaestroDriverGrpc.java:805)
                                                                                                    	at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182)
                                                                                                    	at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:355)
                                                                                                    	at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:867)
                                                                                                    	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
                                                                                                    	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
                                                                                                    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
                                                                                                    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
                                                                                                    	at java.lang.Thread.run(Thread.java:761)
2023-10-23 14:24:51.224 32687-663   System.out              dev.mobile.maestro                   I  <----------------log for bug15315 killProcess() start pid = --------------->32687
2023-10-23 14:24:51.224 32687-663   Process                 dev.mobile.maestro                   E  jiangning stack
                                                                                                    java.lang.RuntimeException: here
                                                                                                    	at android.os.Process.killProcess(Process.java:1167)
                                                                                                    	at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:111)
                                                                                                    	at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)
                                                                                                    	at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1063)
2023-10-23 14:24:51.225 32687-663   System.out              dev.mobile.maestro                   I  <----------------log for bug15315 killProcess() end--------------->
2023-10-23 14:24:51.225 32687-663   Process                 dev.mobile.maestro                   I  Sending signal. PID: 32687 SIG: 9

but my change has made it use getHintOrFallback instead of node.hintText so it's like the error is not picking up the new change made in ViewHierarchy of the maestro-android module.

@testifyqa testifyqa force-pushed the fix/allow-maestro-to-work-on-android-api-level-25-and-lower branch from ff66758 to 1d47a58 Compare October 23, 2023 13:31
@NyCodeGHG
Copy link
Contributor

Can I ask how you are building?

I just used the maestro script in the project root

@testifyqa
Copy link
Contributor Author

testifyqa commented Oct 23, 2023

So I do not think the ./maestro script is including my changes made in ViewHierarchy.kt of the maestro-android module.

java.lang.NoSuchMethodError: No virtual method getHintText()Ljava/lang/CharSequence; in class Landroid/view/accessibility/AccessibilityNodeInfo; or its super classes (declaration of 'android.view.accessibility.AccessibilityNodeInfo' appears in /system/framework/framework.jar:classes2.dex)
                                                                                                    	at dev.mobile.maestro.ViewHierarchy.dumpNodeRec(ViewHierarchy.kt:123)

line 123 with my changes does not refer to the hintText line, it refers to the one above, so the error is definitely talking about ViewHierarchy without my changes in it. I am having a bit of a hard time running with my latest changes in ViewHierarchy included. I'm pretty sure this fixes it but struggling to verify that.

@axelniklasson do you know how I can build and test this locally with my changes in ViewHierarchy.kt in the maestro-android module included when I build and run please? I've had a look at possible gradle tasks to run but it still seems my ViewHierarchy changes are not being included.

There are very little / no docs on building locally. Thanks.

It looks like (and a comment is included) that ViewHierarchy in the maestro-android module is very similar to AccessibilityNodeInfoDumper, with some modifications, one of them being this node.hintText addition. AccessibilityNodeInfoDumper is what UiAutomator2 uses, which works fine on API 25 and lower.

@axelniklasson
Copy link
Collaborator

@artem888 can you please work with @testifyqa and help him test his changes out? Thanks!

@camoles
Copy link

camoles commented Oct 24, 2023

I don't have my API 25 device on hand at the moment

Hello there @testifyqa. If you need help getting an android emulator for api level 25 (or others) running so that your work on this can be made easier, please let us know. It will be a pleasure to help. This way you would not be depending on having a physical device with said version at hand.

@testifyqa
Copy link
Contributor Author

testifyqa commented Oct 24, 2023

Hi there, I have a physical device with API 25 on hand now and also have emulators set up. My specific issue is that my changes made in ViewHierarchy of maestro-android module do not seem to be included in any builds I do locally. Can I please get help on that? More details in my previous comments. Thanks

@NyCodeGHG
Copy link
Contributor

I got it working with ./gradlew assemble assembleAndroidTest, then using the maestro script to run and it successfully ran the advanced sample flow on API Level 25 🥳 .

Also got a lot of warnings because of gradle task dependency issues, seems like this should be improved so the APKs in the resources folder actually get rebuild on changes when using the maestro script

@testifyqa
Copy link
Contributor Author

testifyqa commented Oct 24, 2023

Excellent!! Thanks so much @NyCodeGHG. I went too deep and over looked simply running ./gradlew assemble assembleAndroidTest 🤦

This would be really great to get merged in, not only for my team where I work, but also it will allow many more users to make use of Maestro on lower Android versions.

Edit: Confirmed working for me too with advanced sample flow :)

@NyCodeGHG
Copy link
Contributor

I've tried this also on API 24 which worked fine, but it starts to fail on 23 with this exception

Stacktrace
java.io.IOException: pm list packages --user 0 dev.mobile.maestro
	at maestro.drivers.AndroidDriver.shell(AndroidDriver.kt:917)
	at maestro.drivers.AndroidDriver.isPackageInstalled(AndroidDriver.kt:905)
	at maestro.drivers.AndroidDriver.uninstallMaestroDriverApp(AndroidDriver.kt:872)
	at maestro.drivers.AndroidDriver.installMaestroDriverApp(AndroidDriver.kt:833)
	at maestro.drivers.AndroidDriver.installMaestroApks(AndroidDriver.kt:867)
	at maestro.drivers.AndroidDriver.open(AndroidDriver.kt:75)
	at maestro.Maestro$Companion.android(Maestro.kt:595)
	at maestro.cli.session.MaestroSessionManager.createAndroid(MaestroSessionManager.kt:266)
	at maestro.cli.session.MaestroSessionManager.createMaestro(MaestroSessionManager.kt:149)
	at maestro.cli.session.MaestroSessionManager.access$createMaestro(MaestroSessionManager.kt:49)
	at maestro.cli.session.MaestroSessionManager$newSession$session$1.invoke(MaestroSessionManager.kt:82)
	at maestro.cli.session.MaestroSessionManager$newSession$session$1.invoke(MaestroSessionManager.kt:81)
	at maestro.cli.db.KeyValueStore.withExclusiveLock(KeyValueStore.kt:37)
	at maestro.cli.session.SessionStore.withExclusiveLock(SessionStore.kt:74)
	at maestro.cli.session.MaestroSessionManager.newSession(MaestroSessionManager.kt:81)
	at maestro.cli.session.MaestroSessionManager.newSession$default(MaestroSessionManager.kt:58)
	at maestro.cli.command.TestCommand.call(TestCommand.kt:136)
	at maestro.cli.command.TestCommand.call(TestCommand.kt:46)
	at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
	at picocli.CommandLine.access$1200(CommandLine.java:145)
	at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
	at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
	at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
	at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
	at maestro.cli.DisableAnsiMixin$Companion.executionStrategy(DisableAnsiMixin.kt:22)
	at picocli.CommandLine.execute(CommandLine.java:2058)
	at maestro.cli.AppKt.main(App.kt:117)
Caused by: java.io.IOException: Command failed (shell,v2,raw:pm list packages --user 0 dev.mobile.maestro): closed
	at dadb.adbserver.AdbServer.send$dadb(AdbServer.kt:99)
	at dadb.adbserver.AdbServerDadb.open(AdbServer.kt:138)
	at dadb.Dadb$DefaultImpls.openShell(Dadb.kt:43)
	at dadb.adbserver.AdbServerDadb.openShell(AdbServer.kt:118)
	at dadb.Dadb$DefaultImpls.shell(Dadb.kt:36)
	at dadb.adbserver.AdbServerDadb.shell(AdbServer.kt:118)
	at maestro.drivers.AndroidDriver.shell(AndroidDriver.kt:915)
	... 26 more

@testifyqa
Copy link
Contributor Author

testifyqa commented Oct 27, 2023

Interesting, for my work I only need it working for API level 25 as the lowest.

Can look at fixing it for API 23 as a separate PR possibly and rename the title/description of this one. I would really love to get this one merged as is to unblock us on part of my test strategy.

Again for API 23 and lower seems like a straightforward fix though, probably a slightly different ADB shell command again.

@camoles
Copy link

camoles commented Oct 27, 2023

In case of this being merged as is, this line in build.gradle should also be changed to correctly reflect the minimum android api level in which maestro is supposed to work. And yes, the title have to be corrected.

@testifyqa
Copy link
Contributor Author

Great point, I'm away this weekend but when back home I can change that. If it's an easy fix for API level 23, and API 22 and 21 are the same commands as API 23 then I can see about including that in here possibly.

@NyCodeGHG
Copy link
Contributor

I've tried this also on API 24 which worked fine, but it starts to fail on 23 with this exception

This seems to be related: mobile-dev-inc/dadb#50

@testifyqa
Copy link
Contributor Author

This seems to be related: mobile-dev-inc/dadb#50

Just had a look, this is indeed the issue for API 23 (Marshmallow) and below.

For this PR, I've updated the build.gradle line to represent the minSDK as 24 and above.

@simon-gilmurray
Copy link

This would be great to add more support for older Android versions! 🏆

@axelniklasson axelniklasson assigned ArthurSav and unassigned artem888 Nov 20, 2023
@testifyqa
Copy link
Contributor Author

Any updates on getting approved and merged please? Would do wonders for my test teams.

@ArthurSav
Copy link
Contributor

ArthurSav commented Nov 27, 2023

@testifyqa Can you also generate and commit the apks with:

  • ./gradlew maestro-android:assembleAndroidTest
  • ./gradlew maestro-android:assemble

As of now we don't do it automatically.

After that we'll run a few tests to make sure existing flows don't break and then we can merge.

@testifyqa
Copy link
Contributor Author

testifyqa commented Nov 28, 2023

Thanks @ArthurSav will do that now

EDIT: Done!

@ArthurSav
Copy link
Contributor

@testifyqa I'm getting the following error when trying to run ./maestro test samples/android-advanced-flow.yaml

io.grpc.StatusRuntimeException: INTERNAL: {                                   
            this.hintText                                                     
        } must not be null                                                    
        at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:271)
        at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:252)        
        at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:165)   
        at maestro_android.MaestroDriverGrpc$MaestroDriverBlockingStub.viewHierarchy(MaestroDriverGrpc.java:641)
        at maestro.drivers.AndroidDriver.callViewHierarchy(AndroidDriver.kt:275)
        at maestro.drivers.AndroidDriver.callViewHierarchy(AndroidDriver.kt:289)
        at maestro.drivers.AndroidDriver.callViewHierarchy$default(AndroidDriver.kt:273)
        at maestro.drivers.AndroidDriver.contentDescriptor(AndroidDriver.kt:264)
        at maestro.ViewHierarchy$Companion.from-c1iYVAs(ViewHierarchy.kt:29)  
        at maestro.Maestro.viewHierarchy-prqvCes(Maestro.kt:412)              
        at maestro.Maestro$findElementWithTimeout$element$1.invoke(Maestro.kt:437)
        at maestro.Maestro$findElementWithTimeout$element$1.invoke(Maestro.kt:436)
        at maestro.utils.MaestroTimer.withTimeout(MaestroTimer.kt:16)         
        at maestro.Maestro.findElementWithTimeout(Maestro.kt:436)             
        at maestro.orchestra.Orchestra.findElement(Orchestra.kt:849)
        at maestro.orchestra.Orchestra.findElement$default(Orchestra.kt:831)
        at maestro.orchestra.Orchestra.tapOnElement(Orchestra.kt:752)
        at maestro.orchestra.Orchestra.executeCommand(Orchestra.kt:239)
        at maestro.orchestra.Orchestra.executeSubflowCommands(Orchestra.kt:581)
        at maestro.orchestra.Orchestra.runSubFlow(Orchestra.kt:622)
        at maestro.orchestra.Orchestra.runFlowCommand(Orchestra.kt:490)
        at maestro.orchestra.Orchestra.executeCommand(Orchestra.kt:268)
        at maestro.orchestra.Orchestra.executeCommands(Orchestra.kt:201)
        at maestro.orchestra.Orchestra.runFlow(Orchestra.kt:111)
        at maestro.orchestra.Orchestra.runFlow$default(Orchestra.kt:75)
        at maestro.cli.runner.MaestroCommandRunner.runCommands(MaestroCommandRunner.kt:185)
        at maestro.cli.runner.TestRunner$runSingle$result$1.invoke(TestRunner.kt:54)
        at maestro.cli.runner.TestRunner$runSingle$result$1.invoke(TestRunner.kt:51)
        at maestro.cli.runner.TestRunner.runCatching(TestRunner.kt:145)
        at maestro.cli.runner.TestRunner.runSingle(TestRunner.kt:51)
        at maestro.cli.command.TestCommand$call$1.invoke(TestCommand.kt:177)
        at maestro.cli.command.TestCommand$call$1.invoke(TestCommand.kt:136)
        at maestro.cli.session.MaestroSessionManager.newSession(MaestroSessionManager.kt:101)
        at maestro.cli.session.MaestroSessionManager.newSession$default(MaestroSessionManager.kt:58)
        at maestro.cli.command.TestCommand.call(TestCommand.kt:136)
        at maestro.cli.command.TestCommand.call(TestCommand.kt:46)
        at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
        at picocli.CommandLine.access$1200(CommandLine.java:145)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
        at maestro.cli.DisableAnsiMixin$Companion.executionStrategy(DisableAnsiMixin.kt:22)
        at picocli.CommandLine.execute(CommandLine.java:2058)
        at maestro.cli.AppKt.main(App.kt:117)

@testifyqa
Copy link
Contributor Author

@ArthurSav I can have a look a bit later, I think the PR is old and needs a rebase as I think new src files were added in to maestro-android since

@testifyqa testifyqa force-pushed the fix/allow-maestro-to-work-on-android-api-level-25-and-lower branch from 2f3bbfd to 0a59b88 Compare November 29, 2023 10:58
@testifyqa
Copy link
Contributor Author

testifyqa commented Nov 29, 2023

@ArthurSav I rebased against this original repo as upstream and reassembled the apks and ran the same command on my API 25 device and it ran successfully, can you try again please? Unable to repro locally at the minute.

@testifyqa
Copy link
Contributor Author

Ignore that, able to repro after cleaning the project also I think, investigating

@ArthurSav
Copy link
Contributor

  • Using Emulator with API 33

@testifyqa
Copy link
Contributor Author

Able to repro on more recent API versions locally now, investigating

@testifyqa
Copy link
Contributor Author

@ArthurSav Fixed!! I tested on API 25, 30 and 33. The fix was to fallback to empty string if hintText is null on api level 26+ also as not everything has a hintText value.

@testifyqa testifyqa force-pushed the fix/allow-maestro-to-work-on-android-api-level-25-and-lower branch from c5fddde to 1fbbb10 Compare November 29, 2023 11:48
Copy link
Contributor

@ArthurSav ArthurSav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright everyone, after running some tests this looks ready to be merged. Thanks for you help and contributions!

@ArthurSav ArthurSav merged commit 74c0bc3 into mobile-dev-inc:main Nov 30, 2023
1 check passed
@testifyqa
Copy link
Contributor Author

Woohoo thanks everyone :D

@testifyqa testifyqa deleted the fix/allow-maestro-to-work-on-android-api-level-25-and-lower branch November 30, 2023 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants