Bug 1473313 - Part 1: Set up geckoview build config for androidTest coverage runs. r=nalexander draft
authorTudor-Gabriel Vîjială <tvijiala@mozilla.com>
Tue, 24 Jul 2018 11:44:24 +0100
changeset 824290 eff538e3759daae6e779ee50df7278476cfe3ac4
parent 823465 87bcafe428a4ad6017e59b915581ae00aa863407
push id117866
push userbmo:tvijiala@mozilla.com
push dateMon, 30 Jul 2018 15:39:33 +0000
reviewersnalexander
bugs1473313
milestone63.0a1
Bug 1473313 - Part 1: Set up geckoview build config for androidTest coverage runs. r=nalexander This patch adds JaCoCo as a dependency for the geckoview androidTest configurations, as well as the `mach android archive-geckoview-coverage-artifacts` command. MozReview-Commit-ID: 36jNAzK44g3
build.gradle
build/moz.configure/java.configure
mobile/android/config/mozconfigs/android-api-16-gradle-dependencies/nightly
mobile/android/config/proguard/proguard.cfg
mobile/android/geckoview/build.gradle
mobile/android/gradle.configure
mobile/android/gradle/jacoco_dependencies.gradle
mobile/android/mach_commands.py
--- a/build.gradle
+++ b/build.gradle
@@ -47,16 +47,17 @@ buildscript {
         // For in tree plugins.
         maven {
             url "file://${gradle.mozconfig.topsrcdir}/mobile/android/gradle/m2repo"
         }
     }
 
     ext.kotlin_version = '1.2.41'
     ext.support_library_version = '26.1.0'
+    ext.jacoco_version = '0.8.1'
 
     if (gradle.mozconfig.substs.MOZ_ANDROID_GOOGLE_PLAY_SERVICES) {
         ext.google_play_services_version = '15.0.1'
     }
 
     dependencies {
         classpath 'com.android.tools.build:gradle:3.1.0'
         classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
--- a/build/moz.configure/java.configure
+++ b/build/moz.configure/java.configure
@@ -61,8 +61,15 @@ def javac_version(javac):
                                          stderr=subprocess.STDOUT).rstrip()
         version = Version(output.split(' ')[-1])
         if version < '1.8':
             die('javac 1.8 or higher is required (found %s). '
                 'Check the JAVA_HOME environment variable.' % version)
         return version
     except subprocess.CalledProcessError as e:
         die('Failed to get javac version: %s', e.output)
+
+
+# Java Code Coverage
+# ========================================================
+option('--enable-java-coverage', env='MOZ_JAVA_CODE_COVERAGE', help='Enable Java code coverage')
+
+set_config('MOZ_JAVA_CODE_COVERAGE', depends('--enable-java-coverage')(lambda v: bool(v)))
--- a/mobile/android/config/mozconfigs/android-api-16-gradle-dependencies/nightly
+++ b/mobile/android/config/mozconfigs/android-api-16-gradle-dependencies/nightly
@@ -29,16 +29,19 @@ ac_add_options --disable-tests
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --with-android-min-sdk=16
 ac_add_options --target=arm-linux-androideabi
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
+# Pull code coverage dependencies too.
+ac_add_options --enable-java-coverage
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_MMA=1
 export MOZ_ANDROID_POCKET=1
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
 
 # End ../android-api-16-frontend/nightly.
--- a/mobile/android/config/proguard/proguard.cfg
+++ b/mobile/android/config/proguard/proguard.cfg
@@ -160,16 +160,21 @@
 #-dontnote **,!ch.boye.**,!org.mozilla.gecko.sync.**
 
 -include "play-services-keeps.cfg"
 
 # Don't print spurious warnings from the support library.
 # See: http://stackoverflow.com/questions/22441366/note-android-support-v4-text-icucompatics-cant-find-dynamically-referenced-cl
 -dontnote android.support.**
 
+# Don't warn when classes referenced by JaCoCo are missing when running the build from android-dependencies.
+-dontwarn java.lang.instrument.**
+-dontwarn java.lang.management.**
+-dontwarn javax.management.**
+
 -include "adjust-keeps.cfg"
 
 -include "leakcanary-keeps.cfg"
 
 -include "appcompat-v7-keeps.cfg"
 
 -include "proguard-android.cfg"
 
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -367,8 +367,81 @@ task("generateSDKBindings", type: JavaEx
 
     dependsOn project(':annotations').jar
 }
 
 apply from: "${topsrcdir}/mobile/android/gradle/jacoco_dependencies.gradle"
 if (project.hasProperty('enable_code_coverage')) {
     apply from: "${topsrcdir}/mobile/android/gradle/jacoco_for_junit.gradle"
 }
+
+// Set up code coverage for tests on emulators.
+if (mozconfig.substs.MOZ_JAVA_CODE_COVERAGE) {
+    apply plugin: "jacoco"
+    jacoco {
+        toolVersion = "${project.jacoco_version}"
+    }
+
+    android {
+        jacoco {
+            version = "$jacoco_version"
+        }
+        buildTypes {
+            debug {
+                testCoverageEnabled true
+            }
+        }
+    }
+
+    configurations {
+        // This configuration is used for dependencies that are not needed at compilation or
+        // runtime, but need to be exported as artifacts of the build for usage on the testing
+        // machines.
+        coverageDependency
+    }
+
+    dependencies {
+        androidTestImplementation "org.jacoco:org.jacoco.ant:$jacoco_version"
+        androidTestImplementation "org.jacoco:org.jacoco.core:$jacoco_version"
+        androidTestImplementation "org.jacoco:org.jacoco.report:$jacoco_version"
+
+        // This is required both in the instrumented application classes and the test classes,
+        // so `api` has to be used instead of `androidTestImplementation`.
+        api "org.jacoco:org.jacoco.agent:$jacoco_version:runtime"
+
+        coverageDependency "org.jacoco:org.jacoco.cli:$jacoco_version:nodeps"
+
+        jacocoAgent "org.jacoco:org.jacoco.agent:${project.jacoco_version}"
+        jacocoAnt "org.jacoco:org.jacoco.ant:${project.jacoco_version}"
+    }
+
+    // This task is used by `mach android archive-geckoview-coverage-artifacts`.
+    task copyCoverageDependencies(type: Copy) {
+        from(configurations.coverageDependency) {
+            include 'org.jacoco.cli-*-nodeps.jar'
+            rename { _ -> 'target.jacoco-cli.jar' }
+        }
+        into "$buildDir/coverage"
+    }
+
+    // Generate tasks to archive compiled classfiles for later use with JaCoCo report generation.
+    // One of these tasks is used by `mach android archive-geckoview-coverage-artifacts`.
+    android.libraryVariants.all { variant ->
+        def name = variant.name
+        def compileTask = tasks.getByName("compile${name.capitalize()}JavaWithJavac")
+        task "archiveClassfiles${name.capitalize()}"(type: Zip, dependsOn: compileTask) {
+            description = "Archive compiled classfiles for $name in order to export them as code coverage artifacts."
+            def fileFilter = ['**/androidTest/**',
+                              '**/test/**',
+                              '**/R.class',
+                              '**/R$*.class',
+                              '**/BuildConfig.*',
+                              '**/Manifest*.*',
+                              '**/*Test*.*',
+                              'android/**/*.*']
+            from fileTree(dir: compileTask.destinationDir, excludes: fileFilter)
+            destinationDir = file("${buildDir}/coverage")
+            // Note: This task assumes only one variant of archiveClassfiles* will be used.
+            // Running multiple variants of this task will overwrite the output archive.
+            archiveName = 'target.geckoview_classfiles.zip'
+        }
+    }
+}
--- a/mobile/android/gradle.configure
+++ b/mobile/android/gradle.configure
@@ -261,16 +261,28 @@ def gradle_android_dependencies_tasks(*t
     def isUploadArchives(task):
         return 'uploadArchives' in task
 
     return list(ifilterfalse(isUploadArchives, imap(withoutGeckoBinaries, chain(*tasks))))
 
 set_config('GRADLE_ANDROID_DEPENDENCIES_TASKS', gradle_android_dependencies_tasks)
 
 
+@depends(gradle_android_build_config)
+def gradle_android_archive_geckoview_coverage_artifacts_tasks(build_config):
+    '''Gradle tasks run by |mach android archive-geckoview-coverage-artifacts|.'''
+    return [
+        'geckoview:archiveClassfiles{geckoview.variant.name}'.format(geckoview=build_config.geckoview),
+        'geckoview:copyCoverageDependencies',
+    ]
+
+set_config('GRADLE_ANDROID_ARCHIVE_GECKOVIEW_COVERAGE_ARTIFACTS_TASKS',
+           gradle_android_archive_geckoview_coverage_artifacts_tasks)
+
+
 # Automation uses this to change log levels, not use the daemon, and use
 # offline mode.
 option(env='GRADLE_FLAGS', default='', help='Flags to pass to Gradle.')
 
 @depends('GRADLE_FLAGS')
 def gradle_flags(value):
     return value[0] if value else ''
 
--- a/mobile/android/gradle/jacoco_dependencies.gradle
+++ b/mobile/android/gradle/jacoco_dependencies.gradle
@@ -1,13 +1,11 @@
 /* -*- Mode: Groovy; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-project.ext.jacoco_version = "0.7.8"
-
 dependencies {
-    testImplementation "org.jacoco:org.jacoco.agent:${project.jacoco_version}"
-    testImplementation "org.jacoco:org.jacoco.ant:${project.jacoco_version}"
-    testImplementation "org.jacoco:org.jacoco.core:${project.jacoco_version}"
-    testImplementation "org.jacoco:org.jacoco.report:${project.jacoco_version}"
+    testImplementation "org.jacoco:org.jacoco.agent:$jacoco_version"
+    testImplementation "org.jacoco:org.jacoco.ant:$jacoco_version"
+    testImplementation "org.jacoco:org.jacoco.core:$jacoco_version"
+    testImplementation "org.jacoco:org.jacoco.report:$jacoco_version"
 }
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -416,16 +416,25 @@ class MachCommands(MachCommandBase):
         # We don't want to gate producing dependency archives on clean
         # lint or checkstyle, particularly because toolchain versions
         # can change the outputs for those processes.
         self.gradle(self.substs['GRADLE_ANDROID_DEPENDENCIES_TASKS'] +
                     ["--continue"] + args, verbose=True)
 
         return 0
 
+    @SubCommand('android', 'archive-geckoview-coverage-artifacts',
+                """Archive compiled geckoview classfiles to be used later in generating code coverage reports.""")  # NOQA: E501
+    @CommandArgument('args', nargs=argparse.REMAINDER)
+    def android_archive_geckoview_classfiles(self, args):
+        self.gradle(self.substs['GRADLE_ANDROID_ARCHIVE_GECKOVIEW_COVERAGE_ARTIFACTS_TASKS'] +
+                    ["--continue"] + args, verbose=True)
+
+        return 0
+
     @SubCommand('android', 'archive-geckoview',
                 """Create GeckoView archives.
         See http://firefox-source-docs.mozilla.org/build/buildsystem/toolchains.html#firefox-for-android-with-gradle""")  # NOQA: E501
     @CommandArgument('args', nargs=argparse.REMAINDER)
     def android_archive_geckoview(self, args):
         ret = self.gradle(
             self.substs['GRADLE_ANDROID_ARCHIVE_GECKOVIEW_TASKS'] + ["--continue"] + args,
             verbose=True)