#!/bin/sh
# //===--------------------------- testit ---------------------------------===//
# //
# //                     The LLVM Compiler Infrastructure
# //
# // This file is distributed under the University of Illinois Open Source
# // License. See LICENSE.TXT for details.
# //
# //===--------------------------------------------------------------------===//

currentpath=`pwd`
origpath=$currentpath
currentdir=`basename $currentpath`
while [ $currentdir != "test" ]; do
	if [ $currentdir = "/" ]
	then
		echo "current directory must be in or under \"test\"."
		exit 1
	fi
	cd ..
	currentpath=`pwd`
	currentdir=`basename $currentpath`
done

cd ..
LIBCXX_ROOT=`pwd`
cd $origpath

ANDROID_SUPPORT=$(cd "$LIBCXX_ROOT"/../../../android/support && pwd)

VERBOSE=1

run () {
  if [ "$VERBOSE" -gt 1 ]; then
    echo "COMMAND: $@"
  fi
  case $VERBOSE in
    0|1)
      # Hide command output and errors.
      "$@" >/dev/null 2>&1
      ;;
    2)
      # Only hide command output
      "$@" >/dev/null
      ;;
    *)
      # Show command output and errors.
      "$@"
      ;;
  esac
}

run2 () {
  if [ "$VERBOSE" -gt 2 ]; then
    echo "COMMAND: $@"
  fi
  case $VERBOSE in
    0|1)
      # Hide command output and errors.
      "$@" >/dev/null 2>&1
      ;;
    2)
      # Only hide command output
      "$@" >/dev/null
      ;;
    *)
      # Show command output and errors.
      "$@"
      ;;
  esac
}

# The list of valid target abis supported by this script.
VALID_ABIS="armeabi armeabi-v7a x86 mips"

DO_HELP=
DO_STATIC=
TARGET_ABI=
TARGET_ARCH=
CXX=
for OPT; do
  case $OPT in
    --help|-?)
      DO_HELP=true
      ;;
    --abi=*)
      TARGET_ABI=${OPT##--abi=}
      ;;
    --static)
      DO_STATIC=true
      ;;
    --cxx=*)
      CXX=${OPT##--cxx=}
      ;;
    --verbose)
      VERBOSE=$(( $VERBOSE + 1 ))
      ;;
    -*)
      echo "Unknown option: $OPT. See --help."
      exit 1
      ;;
    *)
      echo "This script doesn't take parameters. See --help."
      exit 1
      ;;
  esac
done

if [ "$DO_HELP" ]; then
  echo \
"Usage: $(basename $0) [options]

This script is used to run the libc++ test suite for Android.
You will need the following things:

  - The prebuild libc++ libraries in your NDK install.
  - A prebuilt Android toolchain in your path.
  - The 'adb' tool in your path.
  - An Android device connected to ADB.

The toolchain and device must match your target ABI. For example, if
you use --abi=armeabi-v7a, your device must run ARMv7-A Android binaries,
and arm-linux-androideabi-g++ will be used to compile all tests, unless
you use --cxx=<command> to override it.

Valid options:
  --help|-?        Display this message.
  --abi=<name>     Specify target ABI. Use --abi=list for list.
  --static         Link against static libc++ library.
  --cxx=<program>  Override C++ compiler/linker.
  --verbose        Increase verbosity.
"
  exit 0
fi

# Check target ABI.
if [ "$TARGET_ABI" = "list" ]; then
  echo "List of valid target ABIs:"
  for ABI in $VALID_ABIS; do
    printf " %s" $ABI
  done
  printf "\n"
  exit 0
fi

if [ -z "$TARGET_ABI" ]; then
  echo "ERROR: Please specify a target ABI (--abi=<name>)."
  exit 1
fi

FOUND_ABI=
for ABI in $VALID_ABIS; do
  if [ "$ABI" = "$TARGET_ABI" ]; then
    FOUND_ABI=true
    break
  fi
done

if [ -z "$FOUND_ABI" ]; then
  echo "ERROR: Invalid abi '$TARGET_ABI'. Must be one of: $VALID_ABIS"
  exit 1
fi

LIBCXX_LIBS=$(cd $LIBCXX_ROOT/.. && pwd)/libs/$TARGET_ABI
for LIB in libc++_static.a libc++_shared.so; do
  if [ ! -f "$LIBCXX_LIBS/$LIB" ]; then
    echo "ERROR: Missing prebuilt library: $LIBCXX_LIBS/$LIB"
    echo "Please run: build/tools/build-cxx-stl.sh --stl=libc++"
    exit 1
  fi
done

# Check or detect C++ toolchain.
TOOLCHAIN_CFLAGS=
TOOLCHAIN_LDFLAGS=
if [ -z "$TOOLCHAIN_PREFIX" ]; then
  # Compute 
  case $TARGET_ABI in
    armeabi)
      TOOLCHAIN_PREFIX=arm-linux-androideabi
      ;;
    armeabi-v7a)
      TOOLCHAIN_PREFIX=arm-linux-androideabi
      TOOLCHAIN_CFLAGS="-march=armv7-a -mfpu=vfpv3-d16"
      TOOLCHAIN_LDFLAGS="-march=armv7-a -Wl,--fix-cortex-a8"
      ;;
    x86)
      TOOLCHAIN_PREFIX=i686-linux-android
      ;;
    mips)
      TOOLCHAIN_PREFIX=mipsel-linux-android
      ;;
    *)
      echo "ERROR: Unknown ABI '$ABI'"
      exit 1
      ;;
  esac
  CXX=$TOOLCHAIN_PREFIX-g++
fi

REAL_CXX=$(which "$CXX" 2>/dev/null)
if [ -z "$REAL_CXX" ]; then
  echo "ERROR: Missing C++ compiler: $CXX"
  exit 1
fi
CC=$CXX

if [ -z "$OPTIONS" ]
then
	OPTIONS="-std=c++11"
fi
OPTIONS="$OPTIONS -I$LIBCXX_ROOT/test/support"

if [ -z "$HEADER_INCLUDE" ]
then
       HEADER_INCLUDE="-I$LIBCXX_ROOT/include -I$ANDROID_SUPPORT/include"
fi

if [ -z "$SOURCE_LIB" ]
then
       SOURCE_LIB="-L$LIBCXX_LIBS"
fi
if [ -z "$ADB" ]
then
       ADB=adb
fi

if [ "$DO_STATIC" ]; then
  # Statically link to ensure the executable can be run easily through ADB
  LIBS=-lc++_static
else
  run2 $ADB push $LIBCXX_LIBS/libc++_shared.so /data/local/tmp 2>/dev/null
  if [ $? != 0 ]; then
    echo "ERROR: Can't push shared libc++ to target device!"
    exit 1
  fi
  LIBS=-lc++_shared
fi

case $TRIPLE in
  *-*-mingw* | *-*-cygwin* | *-*-win*)
    TEST_EXE=test.exe
    ;;
  *)
    TEST_EXE=a.out
    ;;
esac

TEST_EXE=/tmp/testit_android-$USER-$$-$TEST_EXE

FAIL=0
PASS=0
UNIMPLEMENTED=0
IMPLEMENTED_FAIL=0
IMPLEMENTED_PASS=0

# Run a shell command through ADB, return its status.
adb_shell () {
  # We need a temporary file to store the output of our command
  local CMD_OUT RET OUTPUT
  CMD_OUT=$(mktemp /tmp/testit_android-cmdout-XXXXXX)
  # Run the command, while storing the standard output to CMD_OUT
  # and appending the exit code as the last line.
  if [ "$VERBOSE" -gt 2 ]; then
    echo "COMMAND: $ADB shell $@"
  fi
  $ADB shell "$@ ; echo \$?" | sed -e 's![[:cntrl:]]!!g' > $CMD_OUT 2>&1
  # Get last line in log, which contains the exit code from the command
  RET=$(sed -e '$!d' $CMD_OUT)
  # Get output, which corresponds to everything except the last line
  OUT=$(sed -e '$d' $CMD_OUT)
  rm -f $CMD_OUT
  if [ "$VERBOSE" -gt 2 ]; then
    printf "%s" "$OUT"
  fi
  return $RET
}

# Run a given executable through ADB.
# $1: Executable path
# $2+: arguments.
adb_run () {
  local EXECUTABLE EXECUTABLE_BASENAME TARGET_PATH
  EXECUTABLE=$1
  EXECUTABLE_BASENAME=$(basename "$EXECUTABLE")
  shift
  TARGET_PATH=/data/local/tmp
  run2 $ADB push $EXECUTABLE $TARGET_PATH/$EXECUTABLE_BASENAME 2>/dev/null &&
  adb_shell "LD_LIBRARY_PATH=$TARGET_PATH; cd $TARGET_PATH; ./$EXECUTABLE_BASENAME"
}

afunc() {
	fail=0
	pass=0
	if (ls *.fail.cpp > /dev/null 2>&1)
	then
		for FILE in $(ls *.fail.cpp); do
			if run $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS -o $TEST_EXE > /dev/null 2>&1
			then
				rm $TEST_EXE
				echo "$FILE should not compile"
				fail=$(($fail+1))
			else
				pass=$(($pass+1))
			fi
		done
	fi

	if (ls *.pass.cpp > /dev/null 2>&1)
	then
		for FILE in $(ls *.pass.cpp); do
                      if [ "$VERBOSE" -gt 1 ]; then
                          echo "Running test: " $FILE
                      fi
			if run $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS -o $TEST_EXE
			then
				if adb_run $TEST_EXE
				then
					rm $TEST_EXE
					pass=$(($pass+1))
				else
					echo "`pwd`/$FILE failed at run time"
					echo "Compile line was:" $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS
					fail=$(($fail+1))
					rm $TEST_EXE
				fi
			else
				echo "`pwd`/$FILE failed to compile"
				echo "Compile line was:" $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS
				fail=$(($fail+1))
			fi
		done
	fi

	if [ $fail -gt 0 ]
	then
		echo "failed $fail tests in `pwd`"
		IMPLEMENTED_FAIL=$(($IMPLEMENTED_FAIL+1))
	fi
	if [ $pass -gt 0 ]
	then
		echo "passed $pass tests in `pwd`"
		if [ $fail -eq 0 ]
		then
			IMPLEMENTED_PASS=$((IMPLEMENTED_PASS+1))
		fi
	fi
	if [ $fail -eq 0 -a $pass -eq 0 ]
	then
		echo "not implemented:  `pwd`"
		UNIMPLEMENTED=$(($UNIMPLEMENTED+1))
	fi

	FAIL=$(($FAIL+$fail))
	PASS=$(($PASS+$pass))

	for FILE in *
	do
		if [ -d "$FILE" ];
		then
			cd $FILE
			afunc
			cd ..
		fi
	done
}

afunc

echo "****************************************************"
echo "Results for `pwd`:"
echo "using `$CC --version`"
echo "with $OPTIONS $HEADER_INCLUDE $SOURCE_LIB"
echo "----------------------------------------------------"
echo "sections without tests   : $UNIMPLEMENTED"
echo "sections with failures   : $IMPLEMENTED_FAIL"
echo "sections without failures: $IMPLEMENTED_PASS"
echo "                       +   ----"
echo "total number of sections : $(($UNIMPLEMENTED+$IMPLEMENTED_FAIL+$IMPLEMENTED_PASS))"
echo "----------------------------------------------------"
echo "number of tests failed   : $FAIL"
echo "number of tests passed   : $PASS"
echo "                       +   ----"
echo "total number of tests    : $(($FAIL+$PASS))"
echo "****************************************************"

exit $FAIL