#!/bin/bash
#
# Copyright (C) 2007 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.

# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
    newProg=`/bin/ls -ld "${prog}"`
    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
    if expr "x${newProg}" : 'x/' >/dev/null; then
        prog="${newProg}"
    else
        progdir=`dirname "${prog}"`
        prog="${progdir}/${newProg}"
    fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`

skip_tests=""

# Command-line options
sequential="no"
usage="no"
while [[ "$1" == "-"* ]]; do
  case $1 in
    --seq) sequential="yes" ;;
    --skip) skip_tests="$2 $skip_tests"
            shift ;;
    *) usage="yes" ;;
  esac
  shift
done

if [ $usage = "yes" ]; then
    prog=`basename $prog`
    cat 1>&2 <<END_USAGE
Usage:
  $prog [options]   Run all tests with given options.
Options:
  --seq             Run tests sequentially (default: parallel)
  --skip <test>     Skip running specified test
END_USAGE
    exit 1
fi

# Globals for tracking numbers of successes and failures and their names.
passed=()
surprised=()
ignored=()
failed=()
skipped=()

# Tests failing and require attention (e.g. 115-merge)
known_bad="100-local-mismatch 115-merge 119-merge-conflict"

function display_results {
  printf    "\n\nTest Results\n"
  printf -- "----------------------------\n"
  printf    "Pass:                   % 4d\n" ${#passed[@]}
  printf    "Surprise pass:          % 4d\n" ${#surprised[@]}
  printf    "Known failures:         % 4d\n" ${#ignored[@]}
  printf    "Failures:               % 4d\n" ${#failed[@]}
  printf    "Skipped:                % 4d\n" ${#skipped[@]}
  printf -- "----------------------------\n"
  printf    "Elapsed time(s):        % 4d\n" $SECONDS

  list_files "Unexpected successes" ${surprised[@]}
  list_files "Known failures" ${ignored[@]}
  list_files "Failures" ${failed[@]}
  list_files "Skipped" ${skipped[@]}

  needing_attention=$(( ${#failed[@]} + ${#surprised[@]} ))
  exit ${needing_attention}
}

function list_files {
  # Arguments: Title test_name0 test_name1 ... test_nameN
  echo "$1:"
  shift
  if [[ "$1" = "" ]]; then
    echo "  NONE"
    return
  fi
  while [[ "$1" != "" ]]; do
    echo "  $1"
    shift
  done
}

function update_result {
  local -r test_name=$1
  local -r output=$2
  local -r result=$3
  local expectFail

  if [[ "$known_bad" == *"$test_name"* ]]; then
    expectFail=1
  else
    expectFail=0
  fi
  if [ $result = 0 ]; then
    if [[ $expectFail = 0 ]]; then
      passed+=(${test_name})
    else
      echo "Failing on unexpected success of $test_name"
      surprised+=(${test_name})
    fi
  else
    if [[ $expectFail = 0 ]]; then
      failed+=(${test_name})
    else
      echo "Ignoring expected failure of $test_name"
      ignored+=(${test_name})
      # Clean up when we expect a test to fail.
      # run-test only does this on success.
      rm -rf "$output"
    fi
  fi
}

function run_one_test_with_flock {
  local -r output_dir=$1
  local -r test_name=$2
  local -r lock_file=$3

  # Wait for the lock and run the test when acquired
  flock "${lock_file}" ./run-test --output_dir "${output_dir}" "${test_name}"
}

function run_tests {
  readonly test_root=$(mktemp -d)
  trap "rm -rf ${test_root}" EXIT
  if [[ "$sequential" = "yes" ]]; then
    for test_name in *; do
      if [[ "$skip_tests" = *"$test_name"* ]]; then
        skipped+=(${test_name})
        continue
      fi
      if [ -d "$test_name" -a -r "$test_name" ]; then
        output="${test_root}/${test_name}"
        ./run-test --output_dir "${output}" "${test_name}"
        update_result "${test_name}" "${output}" $?
      fi
    done
  else
    readonly num_workers=4
    local i=0
    for test_name in *; do
      if [[ "$skip_tests" = *"$test_name"* ]]; then
        skipped+=(${test_name})
        continue
      fi
      local lock_file=${test_root}/lock.$((i % num_workers))
      if [ -d "${test_name}" -a -r "${test_name}" ]; then
        output="${test_root}/${test_name}"
        run_one_test_with_flock "$output" "$test_name" "$lock_file" &
        test_pids[i]=$!
        test_names[test_pids[i]]="$test_name"
        test_outputs[test_pids[i]]="output"
        let i+=1
      fi
    done

    for pid in ${test_pids[@]}; do
      wait $pid
      update_result ${test_names[$pid]} ${test_outputs[$pid]} $?
    done
  fi
}

function handle_interrupt {
  trap INT
  display_results
  if [ ! -z "${test_pids}" ]; then
    killall ${test_pids}
  fi
}

trap handle_interrupt INT
run_tests
display_results