Blog Infos
Author
Published
Topics
,
Author
Published
Final goal
Requirements for Part 1
Getting started with API 28
public class JavaClass {
int function1() {
return 2 + 2;
}
int function2() {
return 3 + 3;
}
}
view raw JavaClass.java hosted with ❤ by GitHub
class KotlinClass {
fun function1(): Int =
2 + 2
fun function2(): Int =
3 + 3
}
view raw KotlinClass.kt hosted with ❤ by GitHub
@RunWith(AndroidJUnit4::class)
class Tests {
@Test
fun basicTests() {
assertEquals(4, JavaClass().function1())
assertEquals(4, KotlinClass().function1())
}
}
view raw Tests.kt hosted with ❤ by GitHub
Building and running
gcloud firebase test android run \
--type instrumentation \
--no-performance-metrics \
--no-record-video \
--app app/build/outputs/apk/debug/app-debug.apk \
--test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
--device model=Pixel2,version=28,locale=en,orientation=portrait \
--environment-variables coverage=true,coverageFile=/sdcard/Download/coverage.ec \
--directories-to-pull /sdcard/Download
Raw results will be stored in your GCS bucket at [https://console.developers.google.com/storage/browser/test-lab-<project-id>/<timestamp>/]

If you follow the link you’ll come to a page that looks like

 

And then if you click the link for instrumentation.results you’ll come to a page where you can download the contents of the file. It should look something like

 

And if you click the “download” link and scroll to the bottom, you’ll see the results of our attempt to generate a code coverage report…

Error: Failed to generate Emma/JaCoCo coverage. Is Emma/JaCoCo jar on classpath?
Enabling code coverage
buildTypes {
    debug {
        testCoverageEnabled (project.hasProperty('coverage'))
    }
}
Error: Failed to generate Emma/JaCoCo coverage.
E/CoverageListener(6620): Failed to generate Emma/JaCoCo coverage. 
E/CoverageListener(6620): java.lang.reflect.InvocationTargetException
E/CoverageListener(6620): 	at java.lang.reflect.Method.invoke(Native Method)
E/CoverageListener(6620): 	at androidx.test.internal.runner.listener.CoverageListener.generateCoverageReport(CoverageListener.java:101)
E/CoverageListener(6620): 	at androidx.test.internal.runner.listener.CoverageListener.instrumentationRunFinished(CoverageListener.java:70)
E/CoverageListener(6620): 	at androidx.test.internal.runner.TestExecutor.reportRunEnded(TestExecutor.java:92)
E/CoverageListener(6620): 	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:65)
E/CoverageListener(6620): 	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
E/CoverageListener(6620): 	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2145)
E/CoverageListener(6620): Caused by: java.io.FileNotFoundException: /sdcard/Download/coverage.ec (Permission denied)
Granting permission
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.aidan128.coverage1">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Put Your Tests on a Diet:Testing the Behavior and Not the Implementation

How do you write tests? How much time do you spend writing tests? And how much time do you spend fixing them when refactoring?
Watch Video

Put Your Tests on a Diet:Testing the Behavior and Not the Implementation

Stelios Frantzeskakis
Staff Engineer
PSS

Put Your Tests on a Diet:Testing the Behavior and Not the Implementation

Stelios Frantzeska ...
Staff Engineer
PSS

Put Your Tests on a Diet:Testing the Behavior and Not the Implementation

Stelios Frantzes ...
Staff Engineer
PSS

Jobs

One final time, we’ll run gcloud, and now instrumentation.results shows that we’ve succeeded!

Generated code coverage data to /sdcard/Download/coverage.ec

If we look back in Google Cloud Storage (in the same location we saw the instrumentation.results and logcat links) there is now an “artifacts” folder that contains coverage.ec, at last.

 

Supporting Android 10 (API 29)
android {
    compileSdk 29
    defaultConfig {
        targetSdk 29
        ...
    }
    ...
}
gcloud firebase test android run \
--type instrumentation \
--no-performance-metrics \
--no-record-video \
--app app/build/outputs/apk/debug/app-debug.apk \
--test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
--device model=Pixel2,version=29,locale=en,orientation=portrait \
--environment-variables coverage=true,coverageFile=/sdcard/Download/coverage.ec \
--directories-to-pull /sdcard/Download

However, when we run the gcloud command now instrumentation.resultsreports

Error: Failed to generate Emma/JaCoCo coverage

and logcat contains

E/CoverageListener(10124): Failed to generate Emma/JaCoCo coverage. 
E/CoverageListener(10124): java.lang.reflect.InvocationTargetException
E/CoverageListener(10124): 	at java.lang.reflect.Method.invoke(Native Method)
E/CoverageListener(10124): 	at androidx.test.internal.runner.listener.CoverageListener.generateCoverageReport(CoverageListener.java:101)
E/CoverageListener(10124): 	at androidx.test.internal.runner.listener.CoverageListener.instrumentationRunFinished(CoverageListener.java:70)
E/CoverageListener(10124): 	at androidx.test.internal.runner.TestExecutor.reportRunEnded(TestExecutor.java:92)
E/CoverageListener(10124): 	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:65)
E/CoverageListener(10124): 	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
E/CoverageListener(10124): 	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
E/CoverageListener(10124): Caused by: java.io.FileNotFoundException: /sdcard/Download/coverage.ec: open failed: EACCES (Permission denied)

The problem is that API 29 introduces new restrictions on where applications can write to. (“scoped storage”) Luckily, we can easily opt out by adding requestLegacyExternalStorage to app/src/debug/AndroidManifest.xml.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.aidan128.coverage1">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true"
/>
</manifest>
Supporting Android 11 (API 30)
android {
    compileSdk 30

    defaultConfig {
        targetSdk 30
        ...
    }
    ...
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.aidan128.coverage1">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<application
android:requestLegacyExternalStorage="true"
/>
</manifest>
Downloading the .ec file
https://console.developers.google.com/storage/browser/<project-id>/<timestamp>

then the gs:// link for the coverage.ec file (for our chosen device) is

gs://<project-id>/<timestamp>/Pixel2-30-en-portrait/artifacts/coverage.ec

And we can download it by issuing the gsutil command. We’ll put it into app/build/outputs/coverage for now.

mkdir app/build/outputs/coverage
gsutil cp gs://<project-id>/<timestamp>/Pixel2-30-en-portrait/artifacts/coverage.ec app/build/outputs/coverage
Building reports from external .ec files
dependencies {
classpath "org.jacoco:org.jacoco.core:0.8.7"
}
view raw build.gradle hosted with ❤ by GitHub

In our module-level build.gradle, we’ll also need to add a dependency on the JaCoCo plugin as well as add a custom task to build the report.

plugins {
id 'jacoco'
}
...
task jacocoReport(type: JacocoReport) {
group "Coverage"
description "Generate XML/HTML code coverage reports for coverage.ec"
reports {
xml.enabled = true
html.enabled = true
}
getSourceDirectories().setFrom("${project.projectDir}/src/main/java")
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
getClassDirectories().setFrom(
fileTree(dir: "${buildDir}/intermediates/javac/debug", excludes: fileFilter),
fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter))
getExecutionData().setFrom(
fileTree(dir: "${buildDir}/outputs/code_coverage", includes: ['*.ec']))
}
view raw gistfile1.txt hosted with ❤ by GitHub
./gradlew jacocoReport

and then can open the report found at app/build/reports/jacoco/jacocoReport/html/index.html

TL;DR
Next time
Special thanks

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Nowadays authentication has become common in almost all apps. And many of us know…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Every good Android application should be well tested to minimize the risk of error…
READ MORE
blog
Yes! You heard it right. We’ll try to understand the complete OTP (one time…
READ MORE

1 Comment. Leave new

  • Vishal
    23.05.2023 7:41

    I’m doing this thing in Jenkins. So, in command which is used to copy report from gcloud to our report path, in that command we need timestamp, so how to get updated timestamp report because everytime it shows report according to timestamp? Please help me on this.

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu