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

Android CI improvements, improves #2892 #2903

Merged
merged 23 commits into from
Feb 11, 2023
Merged

Android CI improvements, improves #2892 #2903

merged 23 commits into from
Feb 11, 2023

Conversation

TWiStErRob
Copy link
Contributor

@TWiStErRob TWiStErRob commented Feb 6, 2023

followup on #2893 and #2899, fixes #2892

Changes

  • Lock in emulator-build for reproducibility and hopefully stability.
  • Add more emulator-options to make the emulator more stable.
  • Upload instrumentation test results as GHA artifact.
  • Upload logcat from device as GHA artifact.
  • Output relevant logcat to GHA logs in-line.
  • Simplify how the versions are determined.

Investigation details

My first idea (finding) was cold-boot, but apparently that's default behavior via --no-snapshot.

Anyway, I had a look about recent failures, and found the below and made some change in hopes of a more stable CI:

  • Error: Timeout waiting for emulator to boot.

    Job

      The process '/Users/runner/Library/Android/sdk/platform-tools/adb' failed with exit code 1
      /Users/runner/Library/Android/sdk/platform-tools/adb shell getprop sys.boot_completed
      adb: no devices/emulators found
      The process '/Users/runner/Library/Android/sdk/platform-tools/adb' failed with exit code 1
      /Users/runner/Library/Android/sdk/platform-tools/adb shell getprop sys.boot_completed
      adb: no devices/emulators found
      ...
    

    Stumbled upon this issue instrumentation tests uses: reactivecircus/android-emulator-runner@v2 failing - Error: Timeout waiting for emulator to boot. ReactiveCircus/android-emulator-runner#160 in one of the comments in the example CIs. I guess I agree that locking in a known-good version is better than be at the mercy of unstable "stable" releases. Google has blindsided us with a "no-repro" at https://issuetracker.google.com/issues/191799887.

    Fix: added explicit emulator-build

  • Unknown failure: cmd: Can't find service: package

    Job

    > Task :androidTest:connectedDebugAndroidTest
    Exception thrown during onBeforeAll invocation of plugin com.google.testing.platform.plugin.android.AndroidDevicePlugin.
    Failed to install APK(s): /Users/runner/work/mockito/mockito/subprojects/androidTest/build/outputs/apk/androidTest/debug/androidTest-debug-androidTest.apk
    Unknown failure: cmd: Can't find service: package
    com.android.ddmlib.InstallException: Unknown failure: cmd: Can't find service: package
    	at com.android.ddmlib.internal.DeviceImpl.installRemotePackage(DeviceImpl.java:1315)
    	at com.android.ddmlib.internal.DeviceImpl.installPackage(DeviceImpl.java:1141)
    	at com.android.tools.utp.plugins.deviceprovider.ddmlib.DdmlibAndroidDevice.installPackage(DdmlibAndroidDevice.kt)
    	at com.android.tools.utp.plugins.deviceprovider.ddmlib.DdmlibAndroidDeviceController$executeAsync$deferred$1.invokeSuspend(DdmlibAndroidDeviceController.kt:173)
    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
    
    > Task :androidTest:connectedDebugAndroidTest FAILED
    

    Strange error, it looks like the emulator is in a bad state. This could happen because the GitHub Actions runners might be shared between different jobs, and the emulator might be in a bad state when we try to use it. This sounds weird, because I'm sure GHA ensures a clean state, but not sure about it. Anyway, I haven't found any other suggestion than to kill the emulator and cold-boot it. Which is achieved by the default -no-snapshot flag. The only option I see here is to retry somehow.

    Fix: N/A so far, 🤞 emulator-build helps.

  • Unknown failure: cmd: Failure calling service package: Broken pipe (32)

    Job

    > Task :androidTest:connectedDebugAndroidTest
    Exception thrown during onBeforeAll invocation of plugin com.google.testing.platform.plugin.android.AndroidDevicePlugin.
    Failed to install APK(s): /Users/runner/work/mockito/mockito/subprojects/androidTest/build/outputs/apk/debug/androidTest-debug.apk
    Unknown failure: cmd: Failure calling service package: Broken pipe (32)
    com.android.ddmlib.InstallException: Unknown failure: cmd: Failure calling service package: Broken pipe (32)
    	at com.android.ddmlib.internal.DeviceImpl.installRemotePackage(DeviceImpl.java:1315)
    	at com.android.ddmlib.internal.DeviceImpl.installPackage(DeviceImpl.java:1141)
    	at com.android.tools.utp.plugins.deviceprovider.ddmlib.DdmlibAndroidDevice.installPackage(DdmlibAndroidDevice.kt)
    	at com.android.tools.utp.plugins.deviceprovider.ddmlib.DdmlibAndroidDeviceController$executeAsync$deferred$1.invokeSuspend(DdmlibAndroidDeviceController.kt:173)
    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
    
    
    > Task :androidTest:connectedDebugAndroidTest FAILED
    

    Not a clue what's happening here, let's assume it's the "same" problem as above.
    Retry might help in this case, but it's quite complex, not sure that we want to adopt this.
    https://github.com/kiwix/kiwix-android/blob/develop/contrib/instrumentation.sh

    Fix: N/A so far, 🤞 emulator-build helps.

  • Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.

    Job

    > Task :androidTest:connectedDebugAndroidTest
    Starting 6 tests on test(AVD) - 13
    
    org.mockitousage.androidtest.BasicInstrumentedTests > mockAndUseBasicClassWithVerify[test(AVD) - 13] FAILED 
    	org.mockito.exceptions.base.MockitoException:
    	Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.
    
    org.mockitousage.androidtest.BasicInstrumentedTests > mockAndUseBasicClassUsingLocalMock[test(AVD) - 13] FAILED 
    	org.mockito.exceptions.base.MockitoException:
    	Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.
    
    org.mockitousage.androidtest.BasicInstrumentedTests > mockAndUseBasicInterfaceUsingAnnotatedMock[test(AVD) - 13] FAILED 
    	org.mockito.exceptions.base.MockitoException:
    	Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.
    
    org.mockitousage.androidtest.BasicInstrumentedTests > mockAndUseBasicClassUsingAnnotatedMock[test(AVD) - 13] FAILED 
    	org.mockito.exceptions.base.MockitoException:
    	Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.
    
    org.mockitousage.androidtest.BasicInstrumentedTests > mockAndUseBasicInterfaceAndVerify[test(AVD) - 13] FAILED 
    	org.mockito.exceptions.base.MockitoException:
    	Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.
    
    org.mockitousage.androidtest.BasicInstrumentedTests > mockAndUseBasicInterfaceUsingLocalMock[test(AVD) - 13] FAILED 
    	org.mockito.exceptions.base.MockitoException:
    	Mockito cannot mock this class: interface org.mockitousage.androidtest.BasicInterface.
    Tests on test(AVD) - 13 failed: There was 6 failure(s).
    
    > Task :androidTest:connectedDebugAndroidTest FAILED
    

    Undiagnosable as per Set up Android Github Action pipeline. Fixes #2892 #2893 (comment). What we know: the emulator started, the Gradle build completed, and app deployed, and tests are discovered. At this point it's usually the production/test code that's wrong, but not reproducible locally.

    Fix: Added artifact for the test results.


Note: I went through all referenced CIs here:
https://github.com/ReactiveCircus/android-emulator-runner#who-is-using-android-emulator-runner
to find some ideas on how to improve the Mockito CI.
Most of them used verbatim runners as we've set up, but some (major ones) used some extra options.

Additional findings, that I ignored for now:

Checklist

  • Read the contributing guide
  • PR should be motivated, i.e. what does it fix, why, and if relevant how
  • If possible / relevant include an example in the description, that could help all readers
    including project members to get a better picture of the change
  • Avoid other runtime dependencies
  • Meaningful commit history ; intention is important please rebase your commit history so that each
    commit is meaningful and help the people that will explore a change in 2 years
  • The pull request follows coding style
  • Mention Fixes #<issue number> in the description if relevant
  • At least one commit should mention Fixes #<issue number> if relevant

@TWiStErRob TWiStErRob changed the title Android ci improvements Android CI improvements, improves #2892 Feb 6, 2023
@codecov-commenter
Copy link

codecov-commenter commented Feb 6, 2023

Codecov Report

Base: 85.65% // Head: 85.64% // Decreases project coverage by -0.02% ⚠️

Coverage data is based on head (13c747f) compared to base (67259f0).
Patch has no changes to coverable lines.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #2903      +/-   ##
============================================
- Coverage     85.65%   85.64%   -0.02%     
  Complexity     2847     2847              
============================================
  Files           325      325              
  Lines          8623     8623              
  Branches       1060     1060              
============================================
- Hits           7386     7385       -1     
  Misses          964      964              
- Partials        273      274       +1     
Impacted Files Coverage Δ
...l/stubbing/answers/AnswerFunctionalInterfaces.java 93.75% <0.00%> (-1.05%) ⬇️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TWiStErRob TWiStErRob marked this pull request as ready for review February 7, 2023 15:07
@@ -121,8 +117,54 @@ jobs:
with:
arch: x86_64
api-level: ${{ matrix.android-api }}
target: ${{ matrix.android-image-type }}
script: ./gradlew :androidTest:connectedCheck --no-daemon --no-build-cache
# Workaround for https://issuetracker.google.com/issues/267458959
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks massive, is it worth the complexity? (vs building on 26 only)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

26 can have the same issues... it's just how emulators work.

Copy link
Contributor Author

@TWiStErRob TWiStErRob Feb 7, 2023

Choose a reason for hiding this comment

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

btw, most of the complexity is to get the logcat displayed and saved, which is necessary if you want to diagnose issues. This failure looks really ugly: https://github.com/mockito/mockito/actions/runs/4106511182/jobs/7085124984#step:4:674, and the fact that this can happen on this very simple project, means that it can happen randomly for anyone in the world using mockito. So the more info we can get to diagnose failures, the better.

Copy link
Contributor

@TimvdLippe TimvdLippe left a comment

Choose a reason for hiding this comment

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

This is indeed quite a bit of complexity for running a couple of Android tests. That said, if CI is more stable with this, then let's merge this as-is. Thanks for the investigation!

@TimvdLippe TimvdLippe merged commit d6e39b8 into mockito:main Feb 11, 2023
@TWiStErRob TWiStErRob deleted the android-ci-improvements branch February 11, 2023 11:07
@wcandillon
Copy link

@TWiStErRob we are affected by the intermittent Can't find service: package error. We start the emulator build the android app (this can take up to 20min) and then try to install the app which fails 50% of the time. I am wondering if we should build first, start the emulator and then install to improve our chances of the emulator to not be reachable?

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.

Set up Android Github Action pipeline
5 participants