Bug 1433322 - 3. Run GeckoView unit tests with 'mach android test'; r?nalexander draft
authorJim Chen <nchen@mozilla.com>
Thu, 01 Feb 2018 16:48:38 -0500
changeset 750312 4d14b4cef0d98d0156ca4971d6c4de24d25db543
parent 750311 05e73da3a9d4192d2f72184f1b86a7fe32083787
push id97617
push userbmo:nchen@mozilla.com
push dateThu, 01 Feb 2018 21:49:08 +0000
reviewersnalexander
bugs1433322
milestone60.0a1
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: