12a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux#!/bin/bash
22a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux#
32a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Runs robolectric tests.
42a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
52a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxset -euo pipefail
62a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
72a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Terminate with a fatal error.
82a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction fatal() {
92a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  echo "Fatal: $*"
102a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  exit 113
112a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
122a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
132a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Ensures that the given variable is set.
142a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction validate_var() {
152a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local name="$1"; shift || fatal "Missing argument: name"
162a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  test $# = 0 || fatal "Too many arguments"
172a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
182a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  eval [[ -n \${${name}+dummy} ]] || {
192a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    echo "Variable not set: $name";
202a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    return 1;
212a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  }
222a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
232a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
242a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Ensures that all the required variables are set.
252a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction validate_vars() {
262a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  test $# = 0 || fatal "Too many arguments"
272a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
282a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_INTERMEDIATES'
292a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_JARS'
302a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_JAVA_ARGS'
312a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_ROBOLECTRIC_PATH'
322a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_ROBOLECTRIC_SCRIPT_PATH'
332a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_RUN_INDIVIDUALLY'
342a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_TARGET_MESSAGE'
352a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_TESTS'
362a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'PRIVATE_TIMEOUT'
372a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
382a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'XML_OUTPUT_FILE'
392a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_var 'TEST_WORKSPACE'
402a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
412a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
422a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Remove leading and trailing spaces around the given argument.
432a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction strip() {
442a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local value="$1"; shift || fatal "Missing argument: value"
452a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  test $# = 0 || fatal "Too many arguments"
462a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
472a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  echo "$value" | sed -e 's/^ *//' -e 's/ *$//'
482a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
492a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
502a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Normalizes a list of paths and turns it into a colon-separated list.
512a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction normalize-path-list() {
522a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  echo "$@" | sed -e 's/^ *//' -e 's/ *$//' -e 's/  */ /g' -e 's/ /:/g'
532a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
542a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
552a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction junit() {
562a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  # This adds the lib folder to the cp.
572a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local classpath="$(strip "$(normalize-path-list "${PRIVATE_JARS}")")"
5890f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  # Setting the DEBUG_ROBOLECTRIC environment variable will print additional logging from
5990f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  # Robolectric and also make it wait for a debugger to be connected.
6090f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  # For Android Studio / IntelliJ the debugger can be connected via the "remote" configuration:
6190f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  #     https://www.jetbrains.com/help/idea/2016.2/run-debug-configuration-remote.html
6290f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  # From command line the debugger can be connected via
6390f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  #     jdb -attach localhost:5005
6490f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  if [ -n ${DEBUG_ROBOLECTRIC:-""} ]; then
6590f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # The arguments to the JVM needed to debug the tests.
6690f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # - server: wait for connection rather than connecting to a debugger
6790f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # - transport: how to accept debugger connections (sockets)
6890f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # - address: the port on which to accept debugger connections
6990f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # - timeout: how long (in ms) to wait for a debugger to connect
7090f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # - suspend: do not start running any code until the debugger connects
7190f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    local debug_java_args="-Drobolectric.logging.enabled=true \
7290f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam        -Xdebug -agentlib:jdwp=server=y,transport=dt_socket,address=localhost:5005,suspend=y"
7390f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    # Remove the timeout so Robolectric doesn't get killed while debugging
7490f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    local debug_timeout="0"
7590f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam  fi
762a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local command=(
772a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    "${PRIVATE_ROBOLECTRIC_SCRIPT_PATH}/java-timeout"
7890f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    "${debug_timeout:-${PRIVATE_TIMEOUT}}"
7990f0b8e738c88e2da4ced82afd20cdc0ec6fce3fMaurice Lam    ${debug_java_args:-${PRIVATE_JAVA_ARGS}}
802a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    -Drobolectric.dependency.dir="${PRIVATE_ROBOLECTRIC_PATH}"
812a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    -Drobolectric.offline=true
822a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    -Drobolectric.logging=stdout
832a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    -cp "$classpath"
842a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    com.android.junitxml.JUnitXmlRunner
852a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  )
862a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  echo "${command[@]}" "$@"
872a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  "${command[@]}" "$@"
882a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
892a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
902a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction runtests() {
912a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local tests="$1"; shift || fatal "Missing argument: tests"
922a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  test $# = 0 || fatal "Too many arguments"
932a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
942a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  if [[ "$(strip "${PRIVATE_RUN_INDIVIDUALLY}")" = 'true' ]]; then
952a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    local result=0
962a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    for test in ${tests}; do
972a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux      echo "-------------------------------------------------------------------"
982a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux      echo "Running $test:"
992a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux      junit "${test}"
1002a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    done
1012a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    return "$result"
1022a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  else
1032a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    echo "-------------------------------------------------------------------"
1042a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    echo "Running $tests:"
1052a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    junit $tests  # Contains a space-separated list of tests.
1062a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  fi
1072a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
1082a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
1092a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux# Run the robolectric tests
1102a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction run() {
1112a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  test $# = 0 || fatal "Too many arguments"
1122a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
1132a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  [ "${PRIVATE_TARGET_MESSAGE}" == '' ] || echo "${PRIVATE_TARGET_MESSAGE}"
1142a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local tests="${PRIVATE_TESTS}"
1152a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  if [ "$tests" = '' ]; then
1162a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    # Somehow there are no tests to run. Assume this is failure.
1172a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    echo "No tests to run."
1182a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    exit 1
1192a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  fi
1202a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local output="${PRIVATE_INTERMEDIATES}/output.out"
1212a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local failed="${PRIVATE_INTERMEDIATES}/failed.out"
1222a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  local result=0
1232a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  runtests "${tests}" >"$output" 2>&1 || result="$?"
1242a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  echo "$output"
1252a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  cat "$output"
1262a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  if [ "$result" = 0 ]; then
1272a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux    return "$result"
1282a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  fi
1292a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  "${PRIVATE_ROBOLECTRIC_SCRIPT_PATH}/list_failed.sh" <"$output" >"$failed"
1302a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  return "$result"
1312a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
1322a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
1332a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxfunction main() {
1342a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  test $# = 0 || fatal "Too many arguments"
1352a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
1362a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  validate_vars
1372a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux  run
1382a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux}
1392a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieux
1402a2559e5581c585721d342ffe6dceff6c5d7019eJames Lemieuxmain "$@"
141