caipivara
5/26/2017 - 3:01 AM

Android / Gitlab ci - sample setup files to setup your own local gitlab runner with real physical android devices. Check https://github.com/

Android / Gitlab ci - sample setup files to setup your own local gitlab runner with real physical android devices. Check https://github.com/caipivara/awesome-android-scripts

import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

/*
 * Print test results on terminal.
 * From: http://stackoverflow.com/a/36199263/273119
*/
tasks.withType(Test) {
  testLogging {
    events TestLogEvent.FAILED,
        TestLogEvent.PASSED,
        TestLogEvent.SKIPPED,
        TestLogEvent.STANDARD_ERROR,
        TestLogEvent.STANDARD_OUT

    exceptionFormat TestExceptionFormat.FULL

    showCauses true
    showExceptions true
    showStackTraces true
  }
}
// Merge of
// https://github.com/mgouline/android-samples/blob/master/jacoco/app/build.gradle
// and https://github.com/pushtorefresh/storio/blob/master/gradle/jacoco-android.gradle

// Requires Jacoco plugin in build classpath.
apply plugin: 'jacoco'

// Enables code coverage for JVM tests.
// Android Gradle Plugin out of the box supports only code coverage for instrumentation tests.
project.afterEvaluate {
  // Grab all build types and product flavors
  def buildTypes = android.buildTypes.collect { type -> type.name }
  def productFlavors = android.productFlavors.collect { flavor -> flavor.name }

  // When no product flavors defined, use empty
  if (!productFlavors) productFlavors.add('')

  productFlavors.each { productFlavorName ->
    buildTypes.each { buildTypeName ->
      def sourceName, sourcePath
      if (!productFlavorName) {
        sourceName = sourcePath = "${buildTypeName}"
      } else {
        sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
        sourcePath = "${productFlavorName}/${buildTypeName}"
      }
      def testTaskName = "test${sourceName.capitalize()}UnitTest"
      def coverageTaskName = "${testTaskName}Coverage"

      // Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
      task "${coverageTaskName}"(type: JacocoReport, dependsOn: "$testTaskName") {
        group = 'Reporting'
        description = "Generate Jacoco coverage reports for the ${sourceName.capitalize()} build."

        classDirectories = fileTree(
            dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$ViewInjector*.*',
                       '**/*$ViewBinder*.*',
                       '**/BuildConfig.*',
                       '**/Manifest*.*',
                       '**/*$Lambda$*.*', // Jacoco can not handle several "$" in class name.
                       '**/*$inlined$*.*', // Kotlin specific, Jacoco can not handle several "$" in class name.
                       '**/*Module.*', // Modules for Dagger.
                       '**/*Dagger*.*', // Dagger auto-generated code.
                       '**/*MembersInjector*.*', // Dagger auto-generated code.
                       '**/*_Provide*Factory*.*'] // Dagger auto-generated code.
        )

        def coverageSourceDirs = [
            'src/main/java',
            "src/$productFlavorName/java",
            "src/$buildTypeName/java"
        ]

        additionalSourceDirs = files(coverageSourceDirs)
        sourceDirectories = files(coverageSourceDirs)
        executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")

        reports {
          xml.enabled = true
          html.enabled = true
        }
      }

      build.dependsOn "${coverageTaskName}"
    }
  }
}
#!/usr/bin/env bash
#
# Install required dependencies
# sdkmanager can be found in $ANDROID_HOME/tools/bin/sdkmanager
#

# Accept licences
# src http://vgaidarji.me/blog/2017/05/31/automatically-accept-android-sdkmanager-licenses/
/usr/bin/expect -c '
set timeout -1;
spawn '"${ANDROID_HOME}"'/tools/bin/sdkmanager --licenses;
  expect {
    "y/N" { exp_send "y\r" ; exp_continue }
    eof
  }
'

for I in "platforms;android-26" \
         "platforms;android-25" \
         "platforms;android-23" \
         "platforms;android-21" \
         "build-tools;26.0.1" \
         "tools" \
         "platform-tools" \
         "extras;google;m2repository" \
         "extras;android;m2repository" \
         "extras;google;google_play_services"; do
    echo "Trying to update with tools/bin/sdkmanager: " $I
    sdkmanager $I
done

sdkmanager --update
#!/usr/bin/env bash
#
# Copy env variables to app module gradle properties file
#

PROPERTIES_FILE_PATH=gradle.properties

set +x // dont print the next lines on run script

printenv | tr ' ' '\n' > $PROPERTIES_FILE_PATH

set -x
//...

apply plugin: 'spoon'
apply from: "../test-setup.gradle"
apply from: '../jacoco.gradle'

android {
  //...

  defaultConfig {
    //...

    testApplicationId "${appId}.tests"
  }

  buildTypes {
    debug {
      //...
      testCoverageEnabled true
    }
  }
}

spoon {
  //  debug = true
  grantAllPermissions = true
  shard = true
  codeCoverage = true
  //  ignoreFailures = true
  //  failIfNoDeviceConnected = true
}
#!/usr/bin/env bash
#
# Run a command on each adb connected device (adb fail with INSTALL_FAILED_UPDATE_INCOMPATIBLE if multiple devices are connected)
#
# Sample: 
# To uninstall an apk on every device: 
#    $ sh adb-all.sh uninstall my.apk 

adb devices | while read line
do
    if [ ! "$line" = "" ] && [ `echo $line | awk '{print $2}'` = "device" ]
    then
        device=`echo $line | awk '{print $1}'`
        echo "$device $@ ..."
        adb -s $device $@
    fi
done
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.myapp">

  <uses-permission android:name="android.permission.INTERNET" />

  <application
    android:name=".base.MyApp"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <uses-library android:name="android.test.runner" />

    <instrumentation
      android:name="com.myapp.utils.EspressoRxJUnitRunner"
      android:targetPackage="com.myapp.tests" />

  </application>

</manifest>
stages:
  - build
  - test
  - deploy

variables:
  GIT_STRATEGY: clone

cache:
  key: ${CI_PROJECT_ID}
  paths:
    - .gradle/

before_script:
  - sh scripts/cp-env-to-properties.sh

build:
  type: build
  script:
    # Delete spoon screenshots on sdcard before tests to avoid crash
    - sh scripts/adb-all.sh shell rm -r sdcard/app_spoon-screenshots

    - sh scripts/install-android-dependencies.sh
    - ./gradlew clean --stacktrace -PnoDevTools -PpreDexEnable=false
    - ./gradlew assemble  --stacktrace -scan -PnoDevTools -PpreDexEnable=false

    # to avoid INSTALL_FAILED_UPDATE_INCOMPATIBLE
    - sh scripts/adb-all.sh uninstall com.medanswers
    - sh scripts/adb-all.sh uninstall com.medanswers.tests

unit_tests:
  type: test
  script:
    - ./gradlew testDebugUnitTestCoverage  --stacktrace -PnoDevTools -PpreDexEnable=false

      # Print on terminal just the total of the coverage
    - grep -Eo "Total.*?([0–9]{1,3})%" app/build/reports/jacoco/testDebugUnitTestCoverage/html/index.html
  artifacts:
    when: always
    paths:
      - app/build/reports

ui_tests:
  type: test
  script:
    # Wake up screen
    - sh scripts/adb-all.sh shell input keyevent 26 # KEYCODE_POWER
    - sh scripts/adb-all.sh shell input keyevent 3 # HOME
    
    # Wait for each device
    - sh scripts/adb-all.sh wait-for-device
    - sh scripts/adb-all.sh shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'

    - ./gradlew spoonDebugAndroidTest --stacktrace -PnoDevTools -PpreDexEnable=false
    - ./gradlew createDebugCoverageReport --stacktrace -PnoDevTools -PpreDexEnable=false

    # Print on terminal just the total of the coverage
    - grep -Eo "Total.*?([0–9]{1,3})%" app/build/reports/coverage/debug/index.html
  artifacts:
    when: always
    paths:
      - app/build/outputs
      - app/build/spoon
      - app/build/reports

deploy-fabric:
  type: deploy
  environment: staging
  script:
    - fastlane android deploy_staging
  only:
    - /^release.*$/

cache:
  paths:
     - .gradle/wrapper
  key: "$CI_BUILD_REF_NAME-medcache"