mastodon / mastodon-android

Official Android app for Mastodon
https://app.joinmastodon.org/android
GNU General Public License v3.0
1.73k stars 268 forks source link

[Question] how to run the tests? #542

Closed giuliohome closed 1 year ago

giuliohome commented 1 year ago

Thank you for the awesome work. Tried building with Android Studio and the apk was ok (even though I used latest AS and jdk and a fake signature). I only changed the jdk from AS as per your suggestion in another issue and I upgraded the gradle plugin.

My question about the tests because I couldn't run them at first trial: are you using Espresso tests, can them be built and executed from Android Studio, should I tweak anything in gradle settings to make them work?

Thank you again

giuliohome commented 1 year ago

I see I replicated all in my codespaces and my diff is

@giuliohome ➜ /workspaces/mastodon-android (master) $ git diff
diff --git a/build.gradle b/build.gradle
index c61b945e..c4715843 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
                mavenCentral()
        }
        dependencies {
-               classpath "com.android.tools.build:gradle:7.1.3"
+               classpath "com.android.tools.build:gradle:7.4.2"
                classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
                // NOTE: Do not place your application dependencies here; they belong
                // in the individual module build.gradle files
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 464f9805..4c4f8616 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 #Thu Jan 13 11:33:43 MSK 2022
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME

and I guess I have to add --no-daemon for codespaces

@giuliohome ➜ /workspaces/mastodon-android (master) $ ./gradlew connectedAndroidTest --no-daemon
To honour the JVM settings for this build a single-use Daemon process will be forked. See https://docs.gradle.org/7.6/userguide/gradle_daemon.html#sec:disabling_the_daemon.
Daemon will be stopped at the end of the build 
[adb]: * daemon not running; starting now at tcp:5037
[adb]: * daemon started successfully
> Task :mastodon:connectedDebugAndroidTest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':mastodon:connectedDebugAndroidTest'.
> com.android.builder.testing.api.DeviceException: No connected devices!

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1m 2s
56 actionable tasks: 9 executed, 47 up-to-date

and No connected devices! is reasonable in codespaces 😄 I'll try again later on my actual linux laptop only from the shell with ./gradlew connectedAndroidTest, without starting the instrumented tests from android studio, momentarily... But I'd like to know how to run these tests also from Android Studio, if possible.

grishka commented 1 year ago

The tests don't actually test anything. They generate screenshots for Google Play. That's all they do, there are no assertions. You don't need to run them unless you want these screenshots.

giuliohome commented 1 year ago

yes, I understand, I'm just interested in these testing aspects, now I've noticed that there is something like a test instrumented apk (*) - that can be reproduced with the command ./gradlew assembleAndroidTest (working even in codespaces fwiw) - that I could maybe move to a cloud testing environment... OK, thank you, I don't want to waste your time if this is not relevant for the project, we can close this question, keep up with the good work! 😃

(*) well, the apk released from github itself contains the instrumented test hence it looks like it can be directly tested e.g. in firebase test lab (only for robo tests, while instrumented tests need a test apk, see below)

immagine

immagine

giuliohome commented 1 year ago

Cool, I see the Screenshot Clusters from Firebase Test Lab!

immagine

immagine

immagine

etc...

🆒 👍

Edit

The abose was a robo test, not an instrumentation though, so here it is

immagine

immagine

immagine

Running tests locally on my linux laptop

I'll try again later on my actual linux laptop

As promised, now with my mobile connect, but it is failing (not sure why).

[giulio@fedora mastodon-android]$ sudo ./gradlew connectedAndroidTest 

> Task :mastodon:processDebugMainManifest
package="org.joinmastodon.android" found in source AndroidManifest.xml: /home/giulio/android-setup/mastodon-android/mastodon/src/main/AndroidManifest.xml.
Setting the namespace via a source AndroidManifest.xml's package attribute is deprecated.
Please instead set the namespace (or testNamespace) in the module's build.gradle file, as described here: https://developer.android.com/studio/build/configure-app-module#set-namespace
This migration can be done automatically using the AGP Upgrade Assistant, please refer to https://developer.android.com/studio/build/agp-upgrade-assistant for more information.

> Task :mastodon:compileDebugJavaWithJavac
/home/giulio/android-setup/mastodon-android/mastodon/src/main/java/org/joinmastodon/android/model/Poll.java:16: warning: Parceler: Reflection is required to access private field: boolean expired, consider using non-private.
        private boolean expired;
                        ^
/home/giulio/android-setup/mastodon-android/mastodon/src/main/java/org/joinmastodon/android/model/Poll.java:16: warning: Parceler: Reflection is required to modify private field: boolean expired, consider using non-private.
        private boolean expired;
                        ^
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
2 warnings

> Task :mastodon:compileDebugAndroidTestJavaWithJavac
Note: /home/giulio/android-setup/mastodon-android/mastodon/src/androidTest/java/org/joinmastodon/android/test/StoreScreenshotsGenerator.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
07:02:35 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:02:35 V/JdwpProxy-Buffer: DEVICE WRITE (14) JDWP2dHandshake
07:02:35 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:02:35 V/JdwpProxy-Buffer: CLIENT WRITE (14) JDWP2dHandshake
07:02:35 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400000ᅦ1REAQ0000
07:02:35 V/JdwpProxy-Buffer: DEVICE WRITE (23) 00017400010ᅦ1HELO00040001
07:02:35 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400020ᅦ1FEAT0000
07:02:35 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400030ᅦ1MPRQ0000

> Task :mastodon:connectedDebugAndroidTest
Starting 1 tests on Redmi Note 8T - 11

org.joinmastodon.android.test.StoreScreenshotsGenerator > takeScreenshots[Redmi Note 8T - 11] FAILED 
        java.lang.AssertionError: Activity never becomes requested state "[DESTROYED, STARTED, RESUMED, CREATED]" (last lifecycle transition = "PRE_ON_CREATE")
        at androidx.test.core.app.ActivityScenario.waitForActivityToBecomeAnyOf(ActivityScenario.java:353)
Tests on Redmi Note 8T - 11 failed: There was 1 failure(s).

> Task :mastodon:connectedDebugAndroidTest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':mastodon:connectedDebugAndroidTest'.
> There were failing tests. See the report at: file:///home/giulio/android-setup/mastodon-android/mastodon/build/reports/androidTests/connected/index.html

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 6m 29s
56 actionable tasks: 56 executed
giuliohome commented 1 year ago

FYI

this is the log of the exception

image

java.lang.AssertionError: Activity never becomes requested state "[DESTROYED, CREATED, STARTED, RESUMED]" (last lifecycle transition = "PRE_ON_CREATE")
at androidx.test.core.app.ActivityScenario.waitForActivityToBecomeAnyOf(ActivityScenario.java:353)
at androidx.test.core.app.ActivityScenario.launchInternal(ActivityScenario.java:279)
at androidx.test.core.app.ActivityScenario.launch(ActivityScenario.java:197)
at androidx.test.ext.junit.rules.ActivityScenarioRule.lambda$new$0(ActivityScenarioRule.java:73)
at androidx.test.ext.junit.rules.ActivityScenarioRule$$ExternalSyntheticLambda2.get(Unknown Source:2)
at androidx.test.ext.junit.rules.ActivityScenarioRule.before(ActivityScenarioRule.java:106)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:162)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:67)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:438)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2213)

not sure if this is related or which could be the root cause, reopening only to ask your opinion about that.

giuliohome commented 1 year ago

Allowed autostart and pop-up window for this app on my android phone settings. Solved the above exception but it still fails

java.lang.IllegalStateException: Account session null not found
at org.joinmastodon.android.api.session.AccountSessionManager.getAccount(AccountSessionManager.java:141)
at org.joinmastodon.android.test.StoreScreenshotsGenerator.takeScreenshots(StoreScreenshotsGenerator.java:84)

shell cmd

[giulio@fedora mastodon-android]$ sudo ./gradlew connectedAndroidTest 
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (14) JDWP2dHandshake
07:41:06 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400000ᅦ1REAQ0000
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (99) 000cタ0010ᅦ1APNM000P0001103c0p0r0e02d0i0n0i0t0i0a0l0i0z0e0d03e00000001103c0p0r0e02d0i0n0i0t0i0a0l0i0z0e0d03e
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (147) 000モタ0020ᅦ1APNM000タ0001d0o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d02e0t0e0s0t00000001d0o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d02e0t0e0s0t
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (20) 0001440000タ00REAQ00010
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (132) 000ト40001タ00HELO000q000100dh000d00000D0a0l0v0i0k0200v0202e0102e000000000e060402d0b0i0t0200280a0r0m0604029000d0C0h0e0c0k0J0N0I03d0t0r0u0e00000
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (341) 001U40002タ00FEAT001B0007000190h0p0r0o0f02d0h0e0a0p02d0d0u0m0p02d0s0t0r0e0a0m0i0n0g000f0h0p0r0o0f02d0h0e0a0p02d0d0u0m0p000170m0e0t0h0o0d02d0s0a0m0p0l0e02d0p0r0o0f0i0l0i0n0g000200m0e0t0h0o0d02d0t0r0a0c0e02d0p0r0o0f0i0l0i0n0g02d0s0t0r0e0a0m0i0n0g000160m0e0t0h0o0d02d0t0r0a0c0e02d0p0r0o0f0i0l0i0n0g000e0v0i0e0w02d0h0i0e0r0a0r0c0h0y000e0o0p0e0n0g0l02d0t0r0a0c0i0n0g
07:41:06 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400030ᅦ1MPRQ0000
07:41:06 V/JdwpProxy-Buffer: CLIENT WRITE (20) 0001440003タ00MPRQ00010
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:41:12 V/JdwpProxy-Buffer: DEVICE WRITE (14) JDWP2dHandshake
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (14) JDWP2dHandshake
07:41:12 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400050ᅦ1REAQ0000
07:41:12 V/JdwpProxy-Buffer: DEVICE WRITE (23) 00017400060ᅦ1HELO00040001
07:41:12 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400070ᅦ1FEAT0000
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (127) 0007fタ0020ᅦ1APNM000l000180o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d0000000180o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d
07:41:12 V/JdwpProxy-Buffer: DEVICE WRITE (19) 00013400080ᅦ1MPRQ0000
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (20) 0001440005タ00REAQ00010
07:41:12 V/JdwpProxy-Buffer: DEVICE WRITE (20) 00014400090ᅦ1HPIF00010
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (228) 000ffffffe440006タ00HELO000ffffffd10001001016000d000180D0a0l0v0i0k0200v0202e0102e000o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d0000000e060402d0b0i0t0200280a0r0m0604029000d0C0h0e0c0k0J0N0I03d0t0r0u0e0000180o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (341) 001U40007タ00FEAT001B0007000190h0p0r0o0f02d0h0e0a0p02d0d0u0m0p02d0s0t0r0e0a0m0i0n0g000f0h0p0r0o0f02d0h0e0a0p02d0d0u0m0p000170m0e0t0h0o0d02d0s0a0m0p0l0e02d0p0r0o0f0i0l0i0n0g000200m0e0t0h0o0d02d0t0r0a0c0e02d0p0r0o0f0i0l0i0n0g02d0s0t0r0e0a0m0i0n0g000160m0e0t0h0o0d02d0t0r0a0c0e02d0p0r0o0f0i0l0i0n0g000e0v0i0e0w02d0h0i0e0r0a0r0c0h0y000e0o0p0e0n0g0l02d0t0r0a0c0i0n0g
07:41:12 V/JdwpProxy-Buffer: CLIENT WRITE (20) 0001440008タ00MPRQ00010
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:41:13 V/JdwpProxy-Buffer: DEVICE WRITE (14) JDWP2dHandshake
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (4) OKAY
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (14) JDWP2dHandshake
07:41:13 V/JdwpProxy-Buffer: DEVICE WRITE (19) 000134000a0ᅦ1REAQ0000
07:41:13 V/JdwpProxy-Buffer: DEVICE WRITE (23) 000174000b0ᅦ1HELO00040001
07:41:13 V/JdwpProxy-Buffer: DEVICE WRITE (19) 000134000c0ᅦ1FEAT0000
07:41:13 V/JdwpProxy-Buffer: DEVICE WRITE (19) 000134000d0ᅦ1MPRQ0000
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (20) 000144000aタ00REAQ00010
07:41:13 V/JdwpProxy-Buffer: DEVICE WRITE (20) 000144000e0ᅦ1HPIF00010
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (200) 000ffffffc84000bタ00HELO000ᄉ000100103e000d000110D0a0l0v0i0k0200v0202e0102e0003c0p0r0e02d0i0n0i0t0i0a0l0i0z0e0d03e0000000e060402d0b0i0t0200280a0r0m0604029000d0C0h0e0c0k0J0N0I03d0t0r0u0e00001103c0p0r0e02d0i0n0i0t0i0a0l0i0z0e0d03e
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (341) 001U4000cタ00FEAT001B0007000190h0p0r0o0f02d0h0e0a0p02d0d0u0m0p02d0s0t0r0e0a0m0i0n0g000f0h0p0r0o0f02d0h0e0a0p02d0d0u0m0p000170m0e0t0h0o0d02d0s0a0m0p0l0e02d0p0r0o0f0i0l0i0n0g000200m0e0t0h0o0d02d0t0r0a0c0e02d0p0r0o0f0i0l0i0n0g02d0s0t0r0e0a0m0i0n0g000160m0e0t0h0o0d02d0t0r0a0c0e02d0p0r0o0f0i0l0i0n0g000e0v0i0e0w02d0h0i0e0r0a0r0c0h0y000e0o0p0e0n0g0l02d0t0r0a0c0i0n0g
07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (20) 000144000dタ00MPRQ00010

> Task :mastodon:connectedDebugAndroidTest
Starting 1 tests on Redmi Note 8T - 11

07:41:13 V/JdwpProxy-Buffer: CLIENT WRITE (147) 000モタ0020ᅦ1APNM000タ0001d0o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d02e0t0e0s0t00000001d0o0r0g02e0j0o0i0n0m0a0s0t0o0d0o0n02e0a0n0d0r0o0i0d02e0t0e0s0t

> Task :mastodon:connectedDebugAndroidTest

org.joinmastodon.android.test.StoreScreenshotsGenerator > takeScreenshots[Redmi Note 8T - 11] FAILED 
        java.lang.IllegalStateException: Account session null not found
        at org.joinmastodon.android.api.session.AccountSessionManager.getAccount(AccountSessionManager.java:141)
Tests on Redmi Note 8T - 11 failed: There was 1 failure(s).

> Task :mastodon:connectedDebugAndroidTest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':mastodon:connectedDebugAndroidTest'.
> There were failing tests. See the report at: file:///home/giulio/android-setup/mastodon-android/mastodon/build/reports/androidTests/connected/index.html

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 19s
56 actionable tasks: 1 executed, 55 up-to-date
grishka commented 1 year ago

I've never tried running these tests in any kind of server environment or form the command line, so what you're trying to do might never work. I ran them locally on an emulator from within Android Studio.

The last exception says that there's no account logged into the app. You need to install the app, run it normally, and log into an account before you run these tests.

giuliohome commented 1 year ago

Thanks for your reply.

Tried after I've manually logged in with my account and now I see

androidx.test.espresso.PerformException: Error performing 'wait for a specific view with id <2131165208> during 5000 millis.' on view 'Animations or transitions are enabled on the target device.
For more info check: https://developer.android.com/training/testing/espresso/setup#set-up-environment

view.getRootView() to equal view'.
at androidx.test.espresso.PerformException$Builder.build(PerformException.java:1)
at androidx.test.espresso.base.PerformExceptionHandler.handleSafely(PerformExceptionHandler.java:8)
at androidx.test.espresso.base.PerformExceptionHandler.handleSafely(PerformExceptionHandler.java:9)
at androidx.test.espresso.base.DefaultFailureHandler$TypedFailureHandler.handle(DefaultFailureHandler.java:4)
at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:5)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:8)
at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:11)
at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:8)
at org.joinmastodon.android.test.StoreScreenshotsGenerator.takeScreenshots(StoreScreenshotsGenerator.java:103)
... 33 trimmed
Caused by: java.util.concurrent.TimeoutException
at org.joinmastodon.android.test.StoreScreenshotsGenerator$3.perform(StoreScreenshotsGenerator.java:245)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:25)
at androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform(Unknown Source:0)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:5)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:8057)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

I'm running the test locally and on the mobile "record not found" and "retry" shows up... and I see the screenshots saved in the phone... so let's say it is ok :smile_cat: even though it technically fails...

... finally, done, test passed: it has been tricky tweaking the test script, especially finding the id of my account on mastodon.world

activityScenarioRule.getScenario().onActivity(activity->UiUtils.openProfileByID(activity, session.getID(), "109375273836304518"));
Thread.sleep(500);
onView(isRoot()).perform(waitId(R.id.avatar_border, 5000)); // wait for profile to load
onView(isRoot()).perform(waitId(R.id.more, 5000)); // wait for timeline to load
Thread.sleep(500);
takeScreenshot("Profile");