#!/usr/bin/env bash

set -e

function usage {
  cat <<-EOF
USAGE

  $0 [-adDg] [-s SEED] [-l LANGUAGE] [-e EXAMPLE]

OPTIONS

  -h  Print this message

  -a  Compile C code with the Clang address sanitizer

  -e  Run only the corpus tests whose name contain the given string

  -i  Run the given number of iterations of randomized tests (default 10)

  -s  Set the seed used to control random behavior

  -d  Print parsing log to stderr

  -D  Generate an SVG graph of parsing logs

  -g  Run the tests with a debugger

EOF
}

export RUST_BACKTRACE=full

mode=normal
test_flags=""

while getopts "adDghl:e:s:i:" option; do
  case ${option} in
    h)
      usage
      exit
      ;;
    a)
      export CFLAGS="-fsanitize=undefined,address"

      # When the Tree-sitter C library is compiled with the address sanitizer, the address sanitizer
      # runtime library needs to be linked into the final test executable. When using Xcode clang,
      # the Rust linker doesn't know where to find that library, so we need to specify linker flags directly.
      runtime_dir=$(cc -print-runtime-dir)
      if [[ $runtime_dir == */Xcode.app/* ]]; then
        export RUSTFLAGS="-C link-arg=-L${runtime_dir} -C link-arg=-lclang_rt.asan_osx_dynamic -C link-arg=-Wl,-rpath,${runtime_dir}"
      fi

      # Specify a `--target` explicitly. This is required for address sanitizer support.
      toolchain=$(rustup show active-toolchain)
      toolchain_regex='(stable|beta|nightly)-([_a-z0-9-]+).*'
      if [[ $toolchain =~ $toolchain_regex ]]; then
        release=${BASH_REMATCH[1]}
        current_target=${BASH_REMATCH[2]}
      else
        echo "Failed to parse toolchain '${toolchain}'"
      fi

      test_flags+=" --target ${current_target}"
      ;;
    e)
      export TREE_SITTER_EXAMPLE=${OPTARG}
      ;;
    s)
      export TREE_SITTER_SEED=${OPTARG}
      ;;
    i)
      export TREE_SITTER_ITERATIONS=${OPTARG}
      ;;
    d)
      export TREE_SITTER_LOG=1
      ;;
    D)
      export TREE_SITTER_LOG_GRAPHS=1
      ;;
    g)
      mode=debug
      ;;
  esac
done

shift $(expr $OPTIND - 1)

if [[ "${mode}" == "debug" ]]; then
  test_binary=$(
    cargo test $test_flags --no-run --message-format=json 2> /dev/null |\
    jq -rs 'map(select(.target.name == "tree-sitter-cli" and .executable))[0].executable'
  )
  lldb "${test_binary}" -- $1
else
  cargo test $test_flags $1 -- --nocapture
fi
