#!/bin/bash
#
# Generate test coverage statistics for Go packages.
#

set -e

output() {
  color="32"
  if [[ "$2" -gt 0 ]]; then
    color="31"
  fi
  printf "\033[${color}m"
  echo $1
  printf "\033[0m"
}

workdir=".cover"
cover_mode="set"
kernel_name=$(uname -s)
packages=$(go list ./... | grep -v vendor)

show_help() {
cat << EOF
Generate test coverage statistics for Go packages.

  -- Command Flag --
  -h | --help                    Display this help and exit
  -m | --mode                    Set coverage mode. default is "set" (set|count|atomic)
  -d | --dir                     Set store coverage folder (default is ".cover")

  -- Command Action --
  tool                           Install go dependency tools like gocov or golint.
  testing                        Run go testing for all packages
  coverage                       Generate coverage report for all packages
  junit                          Generate coverage xml report for junit plugin
  lint                           Generate Lint report for all packages
  vet                            Generate Vet report for all packages
  cloc                           Generate Count Lines of Code report for all files
  all                            Execute coverage、junit、lint、vet and cloc report

Contribute and source at https://github.com/appleboy/golang-testing
EOF
exit 0
}

# add this function on your .bashrc file to echoed Go version
go_version() {
  if [ "$1" = "" ]; then
    match=1
  else
    match=2
  fi
  version=$(go version)
  regex="go(([0-9].[0-9]).[0-9])"
  if [[ $version =~ $regex ]]; then
    echo ${BASH_REMATCH[${match}]}
  fi
}

set_workdir() {
  workdir=$1
  test -d $workdir || mkdir -p $workdir
  coverage_report="$workdir/coverage.txt"
  coverage_xml_report="$workdir/coverage.xml"
  junit_report="$workdir/junit.txt"
  junit_xml_report="$workdir/report.xml"
  lint_report="$workdir/lint.txt"
  vet_report="$workdir/vet.txt"
  cloc_report="$workdir/cloc.xml"
}

install_dependency_tool() {
  goversion=$(go_version "gloabl")
  [ -d "${GOPATH}/bin" ] || mkdir -p ${GOPATH}/bin
  go get -u github.com/jstemmer/go-junit-report
  go get -u github.com/axw/gocov/gocov
  go get -u github.com/AlekSi/gocov-xml
  if [[ "$goversion" < "1.6" ]]; then
    output "Golint requires Go 1.6 or later."
  else
    go get -u github.com/golang/lint/golint
  fi
  curl https://raw.githubusercontent.com/AlDanial/cloc/master/cloc -o ${GOPATH}/bin/cloc
  chmod 755 ${GOPATH}/bin/cloc
}

errorNumber() {
  if [ "$1" -ne 0 ]; then
    error=$1
  fi
}

testing() {
  error=0
  test -f ${junit_report} && rm -f ${junit_report}
  output "Running ${cover_mode} mode for coverage."
  for pkg in $packages; do
    f="$workdir/$(echo $pkg | tr / -).cover"
    output "Testing coverage report for ${pkg}"
    go test -v -cover -coverprofile=${f} -covermode=${cover_mode} $pkg | tee -a ${junit_report}
    # ref: http://stackoverflow.com/questions/1221833/bash-pipe-output-and-capture-exit-status
    errorNumber ${PIPESTATUS[0]}
  done

  output "Convert all packages coverage report to $coverage_report"
  echo "mode: $cover_mode" > "$coverage_report"
  grep -h -v "^mode:" "$workdir"/*.cover >> "$coverage_report"
  if [ "$error" -ne 0 ]; then
    output "Get Tesing Error Number Code: ${error}" ${error}
  fi
}

generate_cover_report() {
  gocov convert ${coverage_report} | gocov-xml > ${coverage_xml_report}
}

generate_junit_report() {
  cat ${junit_report} | go-junit-report > ${junit_xml_report}
}

generate_lint_report() {
  for pkg in $packages; do
    output "Go Lint report for ${pkg}"
    golint ${pkg} | tee -a ${lint_report}
  done

  # fix path error
  root_path=${PWD//\//\\/}
  [ "$kernel_name" == "Darwin" ] && sed -e "s/${root_path}\(\/\)*//g" -i '' ${lint_report}
  [ "$kernel_name" == "Linux" ] && sed -e "s/${root_path}\(\/\)*//g" -i ${lint_report}
}

generate_vet_report() {
  for pkg in $packages; do
    output "Go Vet report for ${pkg}"
    go vet -n -x ${pkg} | tee -a ${vet_report}
  done
}

generate_cloc_report() {
  cloc --by-file --xml --out=${cloc_report} --exclude-dir=vendor,Godeps,.cover .
}

# set default folder.
set_workdir $workdir

# Process command line...

[ $# -gt 0 ] || show_help

while [ $# -gt 0 ]; do
  case $1 in
    --help | -h)
      show_help
    ;;
    --mode | -m)
      shift
      cover_mode=$1
      test -z ${cover_mode} && show_help
      shift
      test -z $1 && show_help
      ;;
    --dir | -d)
      shift
      workdir=$1
      test -z ${workdir} && show_help
      set_workdir ${workdir}
      shift
      test -z $1 && show_help
      ;;
    tool)
      install_dependency_tool
      shift
      ;;
    testing)
      testing
      shift
      ;;
    coverage)
      generate_cover_report
      shift
      ;;
    junit)
      generate_junit_report
      shift
      ;;
    lint)
      generate_lint_report
      shift
      ;;
    vet)
      generate_vet_report
      shift
      ;;
    cloc)
      generate_cloc_report
      shift
      ;;
    all)
      testing
      generate_cover_report
      generate_junit_report
      generate_lint_report
      generate_vet_report
      generate_cloc_report
      shift
      ;;
    *)
      show_help ;;
  esac
done

if [[ "$error" -gt 0 ]]; then
  exit $error
fi