#!/usr/bin/env bash

# Copyright 2015 Google Inc. All rights reserved.
#
# 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 -uo pipefail
DIR="$(dirname $0)"

if [[ ! -z "${BAZEL_TEST:-}" ]]; then
    export JSONNET_BIN=$(rlocation "${JSONNET_BIN}")
    export JSONNETFMT_BIN=$(rlocation "${JSONNETFMT_BIN}")
fi

source "${DIR}/cmd_tests.source"

OUT_DIR=$(mktemp -d)
trap "rm -rf -- ${OUT_DIR}" EXIT INT TERM

pushd "${DIR}"

[[ -d "${OUT_DIR}" ]] || {
    >&2 echo "OUT_DIR not set to a valid directory"
    exit 1
}
[[ -x "${JSONNET_BIN}" ]] || {
    >&2 echo "JSONNET_BIN does not point to an existing jsonnet binary"
    exit 1
}
[[ -x "${JSONNETFMT_BIN}" ]] || {
    >&2 echo "JSONNETFMT_BIN does not point to an existing jsonnet binary"
    exit 1
}

unset SED_STDOUT SED_STDERR
# A sed command to replace the OUT_DIR at the start of the line.
SED_REPLACE_OUT_DIR="s@^${OUT_DIR%/}@out@;p"

# And now the tests:

# do_test <name> <exit_code> <args...>

# Only check_file additional files if the test otherwise succeeded, to avoid double-counting
# failures.  All tests must write into ${OUT_DIR}/${NAME}/ and nowhere else.

do_test "no_args" 1
do_test "help" 0 --help
do_test "version1" 0 -v
do_test "version2" 0 --version
do_test "bad_out" 1 "test.jsonnet" -o ""
do_test "simple1" 0 "test.jsonnet"
do_test "simple3" 0 -
do_test "simple4" 1 "nosuchfile.jsonnet"
do_test "simple5" 1 "test.jsonnet" "test.jsonnet"
if do_test "simple_out" 0 "test.jsonnet" -o "${OUT_DIR}/simple_out/custom_output"; then
    check_file "simple_out" "${OUT_DIR}/simple_out/custom_output" "simple_out.golden.custom_output"
fi
do_test "exec1" 0 -e "{ a: 1, b: 2, c: 3 }"
if do_test "exec_out" 0 -e "{ a: 1, b: 2, c: 3 }" -o "${OUT_DIR}/exec_out/custom_output"; then
    check_file "exec_out" "${OUT_DIR}/exec_out/custom_output" "exec_out.golden.custom_output"
fi
do_test "double_dash" 0 -e -- -1
do_test "max_stack1" 1 -s 1 -e 'local x = 1; x'
do_test "max_stack2" 1 --max-stack 1 -e 'local x = 1; x'
do_test "max_stack3" 0 --max-stack 2 -e 'local x = 1; x'
do_test "max_stack4" 0 --max-stack 7 -e 'local f(n, c=0) = if n == 0 then c else f(n - 1, c + n) tailstrict; f(100)'
do_test "max_stack5" 1 --max-stack 0 -e 'true'
do_test "max_stack6" 1 --max-stack -1 -e 'true'
do_test "jpath1" 0 --jpath "lib1" -e 'import "lib1_test.jsonnet"'
do_test "jpath2" 0 -J "lib1" -e 'import "lib1_test.jsonnet"'
do_test "jpath3" 0 -J "lib2" -e 'import "lib2_test.jsonnet"'
do_test "jpath4" 0 -J "lib1" -J "lib2" -e 'import "lib2_test.jsonnet"'
do_test "jpath5" 0 -J "lib2" -J "lib1" -e 'import "lib2_test.jsonnet"'
do_test "jpath6" 0 -J "lib2" -J "lib1" -e 'importstr "shared.txt"'
do_test "jpath7" 0 -J "lib1" -J "lib2" -e 'importstr "shared.txt"'
do_test "jpath8" 1 -J "" -e 'true'
do_test "jpath9" 0 -J "lib2" -J "lib1" -e 'importbin "shared.txt"'
do_test "jpath10" 0 -J "lib1" -J "lib2" -e 'importbin "shared.txt"'
do_test "ext1" 0 --ext-str x=1 -e 'std.extVar("x")'
do_test "ext2" 0 -V x=1 -e 'std.extVar("x")'
do_test "ext3" 1 -V y=1 -e 'std.extVar("x")'
do_test "ext4" 0 --ext-code x=1+1 -e 'std.extVar("x")'
do_test "ext5" 0 --ext-str-file "x=test.txt" -e 'std.extVar("x")'
do_test "ext6" 0 --ext-code-file "x=test.jsonnet" -e 'std.extVar("x")'
do_test "ext7" 0 --ext-code-file "x=lib1/lib3_test.jsonnet" -e 'std.extVar("x")'
do_test "tla1" 0 --tla-str x=1 -e 'function(x) x'
do_test "tla2" 0 -A x=1 -e 'function(x) x'
do_test "tla3" 1 -A y=1 -e 'function(x) x'
do_test "tla4" 0 --tla-code x=1+1 -e 'function(x) x'
do_test "tla5" 0 --tla-str-file "x=test.txt" -e 'function(x) x'
do_test "tla6" 0 --tla-code-file "x=test.jsonnet" -e 'function(x) x'
do_test "tla7" 1 -A x=val -e 'function() 3'
do_test "tla8" 0 -A x=val -e '3'
do_test "tla9" 0 -e 'function() 3'
do_test "tla10" 0 --tla-code-file "x=lib1/lib3_test.jsonnet" -e 'function(x) x'
do_test "max_trace1" 1 -t 10 -e 'local f(n, c=0) = if n == 0 then error "whee" else f(n - 1, c + n); f(100)'
do_test "max_trace2" 1 --max-trace 10 -e 'local f(n, c=0) = if n == 0 then error "whee" else f(n - 1, c + n); f(100)'
do_test "max_trace3" 1 -t 20 -e 'local f(n, c=0) = if n == 0 then error "whee" else f(n - 1, c + n); f(100)'
do_test "max_trace4" 1 -t 0 -e 'local f(n, c=0) = if n == 0 then error "whee" else f(n - 1, c + n); f(100)'
do_test "max_trace5" 1 -t -1 -e 'true'

SED_STDOUT="${SED_REPLACE_OUT_DIR}"
if do_test "multi1" 0 -m "${OUT_DIR}/multi1" -e '{ file1: "file1", file2: "file2" }'; then
    check_file "multi1" "${OUT_DIR}/multi1/file1" "multi1.golden.file1"
    check_file "multi1" "${OUT_DIR}/multi1/file2" "multi1.golden.file2"
fi
if do_test "multi2" 0 --multi "${OUT_DIR}/multi2" -e '{ file1: "file1", file2: "file2" }'; then
    check_file "multi2" "${OUT_DIR}/multi2/file1" "multi2.golden.file1"
    check_file "multi2" "${OUT_DIR}/multi2/file2" "multi2.golden.file2"
fi
unset -v SED_STDOUT
if do_test "multi3" 0 -m "${OUT_DIR}/multi3" -o "${OUT_DIR}/multi3/list" -e '{ file1: "file1", file2: "file2" }'; then
    check_file "multi3" "${OUT_DIR}/multi3/file1" "multi3.golden.file1"
    check_file "multi3" "${OUT_DIR}/multi3/file2" "multi3.golden.file2"
    check_file_sed "multi3" "${OUT_DIR}/multi3/list" "multi3.golden.list" "${SED_REPLACE_OUT_DIR}"
fi

do_test "nonewline1" 0 --no-trailing-newline -e '42'
do_test "nonewline2" 0 --no-trailing-newline test.jsonnet
do_test "nonewline3" 0 --no-trailing-newline --string -e '"hello"'
SED_STDOUT="${SED_REPLACE_OUT_DIR}"
if do_test "nonewline_multi1" 0 --no-trailing-newline --multi "${OUT_DIR}/nonewline_multi1" -e '{ file1: "file1", file2: "file2" }'; then
    check_file "nonewline_multi1" "${OUT_DIR}/nonewline_multi1/file1" "nonewline_multi1.golden.file1"
    check_file "nonewline_multi1" "${OUT_DIR}/nonewline_multi1/file2" "nonewline_multi1.golden.file2"
fi
if do_test "nonewline_multi2" 0 --no-trailing-newline --multi "${OUT_DIR}/nonewline_multi2" --string -e '{ file1: "file1", file2: "file2" }'; then
    check_file "nonewline_multi2" "${OUT_DIR}/nonewline_multi2/file1" "nonewline_multi2.golden.file1"
    check_file "nonewline_multi2" "${OUT_DIR}/nonewline_multi2/file2" "nonewline_multi2.golden.file2"
fi
unset -v SED_STDOUT
do_test "nonewline_yaml1" 1 --no-trailing-newline --yaml-stream -e '[{a:1},{b:2},{c:3}]'

do_test "multi4" 1 -m -- -e 'null'
do_test "yaml1" 0 -y -e '[1,2,3]'
do_test "yaml2" 1 -y -e 'null'
if do_test "yaml3" 0 -y -o "${OUT_DIR}/yaml3/stream" -e '[1,2,3]'; then
    check_file "yaml3" "${OUT_DIR}/yaml3/stream" "yaml3.golden.stream"
fi
do_test "string1" 0 -S -e '"A long\nparagraph."'
do_test "string2" 1 -S -e 'null'

export JSONNET_PATH=lib1:lib2
do_test "jsonnet_path1" 0 -e '[importstr "shared.txt", importbin "shared.txt"]'
export JSONNET_PATH=lib2:lib1
do_test "jsonnet_path2" 0 -e '[importstr "shared.txt", importbin "shared.txt"]'

if [ -z "${DISABLE_FMT_TESTS:-}" ]; then

do_fmt_test "fmt_no_args" 1
do_fmt_test "fmt_help" 0 --help
do_fmt_test "fmt_version1" 0 -v
do_fmt_test "fmt_version2" 0 --version
do_fmt_test "fmt_bad_out" 1 "test.jsonnet" -o ""
do_fmt_test "fmt_simple1" 0 "test.jsonnet"
do_fmt_test "fmt_simple3" 0 -
do_fmt_test "fmt_simple4" 1 "nosuchfile.jsonnet"
do_fmt_test "fmt_simple5" 1 "test.jsonnet" "test.jsonnet"
if do_fmt_test "fmt_simple_out" 0 "test.jsonnet" -o "${OUT_DIR}/fmt_simple_out/custom_output"; then
    check_file "fmt_simple_out" "${OUT_DIR}/fmt_simple_out/custom_output" "fmt_simple_out.golden.custom_output"
fi
do_fmt_test "fmt1" 0 -e "{ a: 1, b: 2, c: 3 }"
if do_fmt_test "fmt_out" 0 -e "{ a: 1, b: 2, c: 3 }" -o "${OUT_DIR}/fmt_out/custom_output"; then
    check_file "fmt_out" "${OUT_DIR}/fmt_out/custom_output" "fmt_out.golden.custom_output"
fi
do_fmt_test "fmt_double_dash" 0 -e -- -1

do_fmt_test "fmt_simple_test1" 0 --test "test.jsonnet"
do_fmt_test "fmt_simple_test2" 2 --test -e "{a:1,b:2,c:3}"
do_fmt_test "fmt_simple_test3" 0 --test -e $'42\n'
do_fmt_test "fmt_simple_test4" 0 --test -
do_fmt_test "fmt_simple_test5" 2 --test -
do_fmt_test "fmt_simple_test6" 2 --test "test.jsonnet" "test_badfmt.jsonnet"

SED_STDOUT="${SED_REPLACE_OUT_DIR}"
if mkdir -p "${OUT_DIR}/fmt_inplace" && cp "test.jsonnet" "test_badfmt.jsonnet" "${OUT_DIR}/fmt_inplace/"; then
    # Test jsonnetfmt in-place modifications
    check_file "fmt_inplace" "${OUT_DIR}/fmt_inplace/test.jsonnet" "fmt_simple_out.golden.custom_output"
    # Verify that running jsonnetfmt on an already formatted file does not change timesetamps
    touch -m -t 199108252057.08 "${OUT_DIR}/fmt_inplace/test.jsonnet"
    touch -m -t 199108252057.08 "${OUT_DIR}/fmt_inplace/test_badfmt.jsonnet"
    stat -c '%y' "${OUT_DIR}/fmt_inplace/test.jsonnet" > "${OUT_DIR}/fmt_inplace/stat_mod_time_before.txt"
    do_fmt_test "fmt_inplace" 0 -i \
        "${OUT_DIR}/fmt_inplace/test.jsonnet" "${OUT_DIR}/fmt_inplace/test_badfmt.jsonnet"
    stat -c '%y' "${OUT_DIR}/fmt_inplace/test.jsonnet" > "${OUT_DIR}/fmt_inplace/stat_mod_time_after.txt"
    check_file "fmt_inplace" "${OUT_DIR}/fmt_inplace/stat_mod_time_before.txt" "${OUT_DIR}/fmt_inplace/stat_mod_time_after.txt"
fi
unset -v SED_STDOUT

fi


popd

# Bean counting:

if [ $FAILED -eq 0 ] ; then
    echo "$0: All $EXECUTED test scripts pass."
else
    echo "$0: FAILED: $FAILED / $EXECUTED"
    exit 1
fi
