Bug 1433322 - 3. Run GeckoView unit tests with 'mach android test'; r=nalexander
authorJim Chen <nchen@mozilla.com>
Thu, 08 Feb 2018 13:36:22 -0500
changeset 402942 e91adf8f9294f2a9cd6d618da2cebbb80076d7a8
parent 402941 0a74b6699ff339025508e0fb7d44b55ce37d5641
child 402943 f676e27cc14585bca5b78ef9f34f617e23003833
push id99691
push usernchen@mozilla.com
push dateThu, 08 Feb 2018 18:36:53 +0000
treeherdermozilla-inbound@22a2b0f1b1ab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1433322
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1433322 - 3. Run GeckoView unit tests with 'mach android test'; r=nalexander Run unit tests under geckoview/ when running 'mach android test'. This also lets us run those tests on Taskcluster. The test report parser for 'mach android test' had a bug where the input directory was wrong. As a result, we weren't producing test output at all. This patch fixes the input directory, and outputs an error if no reports are found at all to avoid this bug in the future. MozReview-Commit-ID: IiswQaSPCr0
mobile/android/geckoview/build.gradle
mobile/android/gradle.configure
mobile/android/mach_commands.py
taskcluster/ci/build/android-stuff.yml
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -143,16 +143,20 @@ android {
             }
         }
     }
 }
 
 dependencies {
     implementation "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     implementation "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+
+    testImplementation 'junit:junit:4.12'
+    testImplementation 'org.robolectric:robolectric:3.5.1'
+    testImplementation 'org.mockito:mockito-core:1.10.19'
 }
 
 apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
 
 android.libraryVariants.all { variant ->
     // See the notes in mobile/android/app/build.gradle for details on including
     // Gecko binaries and the Omnijar.
     if ((variant.productFlavors*.name).contains('withGeckoBinaries')) {
--- a/mobile/android/gradle.configure
+++ b/mobile/android/gradle.configure
@@ -63,27 +63,32 @@ def gradle_android_build_config():
         ),
         geckoview_example=namespace(
             variant=variant(('official', 'withGeckoBinaries', 'noMinApi'), 'debug'),
         ),
     )
 
 
 @depends(gradle_android_build_config)
-def gradle_android_app_variant_name(build_config):
+def gradle_android_variant_name(build_config):
     '''Like "officialPhotonDebug".'''
     def uncapitalize(s):
         if s:
             return s[0].lower() + s[1:]
         else:
             return s
 
-    return uncapitalize(build_config.app.variant.name)
+    return namespace(
+        app=uncapitalize(build_config.app.variant.name),
+        geckoview=uncapitalize(build_config.geckoview.variant.name),
+    )
 
-set_config('GRADLE_ANDROID_APP_VARIANT_NAME', gradle_android_app_variant_name)
+set_config('GRADLE_ANDROID_APP_VARIANT_NAME', gradle_android_variant_name.app)
+
+set_config('GRADLE_ANDROID_GECKOVIEW_VARIANT_NAME', gradle_android_variant_name.geckoview)
 
 
 @depends(gradle_android_build_config)
 def gradle_android_app_tasks(build_config):
     '''Gradle tasks run by |mach android assemble-app|.'''
     return [
         'geckoview:generateJNIWrappersForGenerated{geckoview.variant.name}'.format(geckoview=build_config.geckoview),
         'app:generateJNIWrappersForFennec{app.variant.name}'.format(app=build_config.app),
@@ -133,16 +138,18 @@ set_config('GRADLE_ANDROID_APP_APK', gra
 set_config('GRADLE_ANDROID_APP_ANDROIDTEST_APK', gradle_android_app_apks.app_androidTest_apk)
 
 
 @depends(gradle_android_build_config)
 def gradle_android_test_tasks(build_config):
     '''Gradle tasks run by |mach android test|.'''
     return [
         'app:test{app.variant.name}UnitTest'.format(app=build_config.app),
+        'geckoview:test{geckoview.variant.name}UnitTest'.format(
+            geckoview=build_config.geckoview),
     ]
 
 set_config('GRADLE_ANDROID_TEST_TASKS', gradle_android_test_tasks)
 
 
 @depends(gradle_android_build_config)
 def gradle_android_lint_tasks(build_config):
     '''Gradle tasks run by |mach android lint|.'''
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -66,34 +66,51 @@ class MachCommands(MachCommandBase):
 
     @SubCommand('android', 'test',
         """Run Android local unit tests.
         See https://developer.mozilla.org/en-US/docs/Mozilla/Android-specific_test_suites#android-test""")
     @CommandArgument('args', nargs=argparse.REMAINDER)
     def android_test(self, args):
         ret = self.gradle(self.substs['GRADLE_ANDROID_TEST_TASKS'] + ["--continue"] + args, verbose=True)
 
-        # Findbug produces both HTML and XML reports.  Visit the
+        ret |= self._parse_android_test_results('public/app/unittest', 'gradle/build/mobile/android/app',
+                                                (self.substs['GRADLE_ANDROID_APP_VARIANT_NAME'],))
+
+        ret |= self._parse_android_test_results('public/geckoview/unittest', 'gradle/build/mobile/android/geckoview',
+                                                (self.substs['GRADLE_ANDROID_GECKOVIEW_VARIANT_NAME'],))
+
+        return ret
+
+    def _parse_android_test_results(self, artifactdir, gradledir, variants):
+        # Unit tests produce both HTML and XML reports.  Visit the
         # XML report(s) to report errors and link to the HTML
         # report(s) for human consumption.
         import itertools
         import xml.etree.ElementTree as ET
 
         from mozpack.files import (
             FileFinder,
         )
 
+        ret = 0
+        found_reports = False
+
         root_url = self._root_url(
-            artifactdir='public/android/unittest',
-            objdir='gradle/build/mobile/android/app/reports/tests')
+            artifactdir=artifactdir,
+            objdir=gradledir + '/reports/tests')
 
-        reports = (self.substs['GRADLE_ANDROID_APP_VARIANT_NAME'],)
-        for report in reports:
-            finder = FileFinder(os.path.join(self.topobjdir, 'gradle/build/mobile/android/app/test-results/', report))
+        def capitalize(s):
+            # Can't use str.capitalize because it lower cases trailing letters.
+            return (s[0].upper() + s[1:]) if s else ''
+
+        for variant in variants:
+            report = 'test{}UnitTest'.format(capitalize(variant))
+            finder = FileFinder(os.path.join(self.topobjdir, gradledir + '/test-results/', report))
             for p, _ in finder.find('TEST-*.xml'):
+                found_reports = True
                 f = open(os.path.join(finder.base, p), 'rt')
                 tree = ET.parse(f)
                 root = tree.getroot()
 
                 # Log reports for Tree Herder "Job Details".
                 print('TinderboxPrint: report<br/><a href="{}/{}/index.html">HTML {} report</a>, visit "Inspect Task" link for details'.format(root_url, report, report))
 
                 # And make the report display as soon as possible.
@@ -127,16 +144,20 @@ class MachCommands(MachCommandBase):
                         for line in ET.tostring(skipped).strip().splitlines():
                             print('TEST-INFO | {} | {}'.format(name, line))
 
                     if not error_count:
                         print('TEST-PASS | {}'.format(name))
 
                 print('SUITE-END | android-test | {} {}'.format(report, root.get('name')))
 
+        if not found_reports:
+            print('TEST-UNEXPECTED-FAIL | android-test | No reports found under {}'.format(gradledir))
+            return 1
+
         return ret
 
 
     @SubCommand('android', 'lint',
         """Run Android lint.
         See https://developer.mozilla.org/en-US/docs/Mozilla/Android-specific_test_suites#android-lint""")
     @CommandArgument('args', nargs=argparse.REMAINDER)
     def android_lint(self, args):
--- a/taskcluster/ci/build/android-stuff.yml
+++ b/taskcluster/ci/build/android-stuff.yml
@@ -10,19 +10,22 @@ android-test/opt:
         symbol: A(test)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
         docker-image: {in-tree: android-build}
         env:
             GRADLE_USER_HOME: "/builds/worker/workspace/build/src/mobile/android/gradle/dotgradle-offline"
             PERFHERDER_EXTRA_OPTIONS: android-test
         artifacts:
-            - name: public/android/unittest
+            - name: public/app/unittest
               path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/app/reports/tests
               type: directory
+            - name: public/geckoview/unittest
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/reports/tests
+              type: directory
             - name: public/build
               path: /builds/worker/artifacts/
               type: directory
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build]
         config: