#!/bin/bash
#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Analyze the output from Android Auto app launch test application.

# ITERATION_COUNT decides how many times the test is repeated.
readonly ITERATION_COUNT=2
# SCALE decides the precision of fractional part in bc.
readonly SCALE=9

####################################################
# Calculate arithmetic mean and standard deviation.
# Globals:
#   None
# Arguments:
#   A sequence of numbers
# Returns:
#   Count, average and stddev in space-saparated string
####################################################
function calc_stat() {
  local sum=0
  local count=0
  local mean=0
  local diff_sqrd_sum=0
  local stddev=0

  # Calculate the mean.
  for val in "$@"; do
    sum=$(echo "scale=${SCALE};${sum} + ${val}" | bc)
    ((count++))
  done
  mean=$(echo "scale=${SCALE};${sum} / ${count}" | bc)

  # Calculate standard deviation.
  for val in "$@"; do
    diff_sqrd_sum=$(echo "scale=${SCALE};${diff_sqrd_sum} + (${mean} - ${val}) ^ 2" | bc)
  done
  stddev=$(echo "scale=${SCALE};sqrt(${diff_sqrd_sum} / ${count})" | bc)

  echo "${count} ${mean} ${stddev}"
}

####################################################
# Execute app launch performance test.
# Globals:
#   None
# Arguments:
#   Type of app launch, cold or hot
#   Whether to press home button, boolean
#   Whether to drop cache, boolean
#   Whether to kill app, boolean
# Outputs:
#   Writes the analysis result to stdout
####################################################
function run_app_launch_test() {
  local stat=
  local package_pattern="INSTRUMENTATION_STATUS: $1_startup_([a-z]+(\.[a-z]+)+)=([0-9]+)"
  local cmd="adb shell am instrument -w -r -e iterations ${ITERATION_COUNT}\
    -e listener android.device.collectors.AppStartupListener\
    -e class 'android.platform.test.scenario.dial.OpenAppMicrobenchmark,android.platform.test.scenario.googleplay.OpenAppMicrobenchmark,android.platform.test.scenario.maps.OpenAppMicrobenchmark,android.platform.test.scenario.radio.OpenAppMicrobenchmark,android.platform.test.scenario.settings.OpenAppMicrobenchmark'\
    -e favor-shell-commands true -e log false -e suite-timeout_msec 36000000\
    -e durationMs 30000 -e press-home $2 -e newRunListenerMode true\
    -e timeout_msec 300000 -e drop-cache $3 -e kill-app $4 android.platform.test.scenario/androidx.test.runner.AndroidJUnitRunner"
  # Example output from ${cmd}
  #
  #   INSTRUMENTATION_STATUS: class=android.platform.test.scenario.maps.OpenAppMicrobenchmark
  #   INSTRUMENTATION_STATUS: current=1
  #   INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
  #   INSTRUMENTATION_STATUS: numtests=25
  #   INSTRUMENTATION_STATUS: stream=
  #   android.platform.test.scenario.maps.OpenAppMicrobenchmark:
  #   INSTRUMENTATION_STATUS: test=testOpen
  #   INSTRUMENTATION_STATUS_CODE: 1
  #   INSTRUMENTATION_STATUS: cold_startup_com.google.android.apps.maps=2286
  #   INSTRUMENTATION_STATUS: cold_startup_count_com.google.android.apps.maps=1
  #   INSTRUMENTATION_STATUS: cold_startup_total_count=1
  #   INSTRUMENTATION_STATUS_CODE: 2
  #   INSTRUMENTATION_STATUS: class=android.platform.test.scenario.maps.OpenAppMicrobenchmark
  #   INSTRUMENTATION_STATUS: current=1
  #   INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
  #   INSTRUMENTATION_STATUS: numtests=25
  #   INSTRUMENTATION_STATUS: stream=.
  #   INSTRUMENTATION_STATUS: test=testOpen
  #   INSTRUMENTATION_STATUS_CODE: 0

  declare -A app_launch_map
  printf "Testing $1 start performance....\n"

  while IFS= read -r line; do
    if [[ ${line} =~ ${package_pattern} ]]; then
      APP_PACKAGE="${BASH_REMATCH[1]}"
      LAUNCH_TIME=${BASH_REMATCH[3]}
      app_launch_map[${APP_PACKAGE}]+="${LAUNCH_TIME} "
    fi
  done < <(${cmd})

  for key in "${!app_launch_map[@]}"; do
    stat=($(calc_stat ${app_launch_map[${key}]}))
    printf "[${key}]\n"
    printf "  Count: ${stat[0]}\n"
    printf "  Average: ${stat[1]}\n"
    printf "  StdDev: ${stat[2]}\n"
    printf "\n"
  done
}

# We test two types of app launch performance: cold and hot.
# Cold start is to launch an app without cache support as if it is executed for the first time.
# Hot start is to make an app go into the foreground while the app is running in the background.
run_app_launch_test cold false true true
echo ""
run_app_launch_test hot true false false
