########################################################################
#                                                                      #
#               This software is part of the ast package               #
#          Copyright (c) 1982-2011 AT&T Intellectual Property          #
#                      and is licensed under the                       #
#                 Eclipse Public License, Version 1.0                  #
#                    by AT&T Intellectual Property                     #
#                                                                      #
#                A copy of the License is available at                 #
#          http://www.eclipse.org/org/documents/epl-v10.html           #
#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
#                                                                      #
#              Information and Software Systems Research               #
#                            AT&T Research                             #
#                           Florham Park NJ                            #
#                                                                      #
#                    David Korn <dgkorn@gmail.com>                     #
#                                                                      #
########################################################################
#
# Written by Roland Mainz <roland.mainz@nrubsig.org>
#

set -o nounset

typeset -T test_t=(
    typeset name
    typeset cmd
    typeset expected_output
)

function testfunc
{
    integer line_number=$1
    typeset cmd="$2"
    typeset expected_output="$3"
    typeset output

    output="$($SHELL -c "${cmd}" 2>&1 )"

    [[ "${output}" == "${expected_output}" ]] || log_error ${line_number} "${output} != ${expected_output}"
}

# Test1: Basic tests
function test1
{
    # string
    testfunc ${LINENO} '(function l { typeset -S x ;     x+="#" ; $1 && print "$x" ; } ; l false ; l false   ; l true)'  "###"
    testfunc ${LINENO} 'function  l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false ; l false   ; l true'   ">###"
    testfunc ${LINENO} 'function  l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false ; (l false) ; l true'   ">##"
    testfunc ${LINENO} 'function  l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false; ( ulimit -c 0 ; l false) ; l true' ">##"

    # integer
    # (normal)
    testfunc ${LINENO} '(function l { integer -S x ;        x+=1 ;   $1 && print "$x" ; } ; l false ; l false   ; l true )' "3"
    testfunc ${LINENO} '(function l { integer -S x ;        x+=1 ;   $1 && print "$x" ; } ; l false ; (l false) ; l true )' "2"
    # (int)
    testfunc ${LINENO} '(function l { typeset -S -i x ;     x+=1 ;   $1 && print "$x" ; } ; l false ; l false   ; l true )' "3"
    testfunc ${LINENO} '(function l { typeset -S -i x ;     x+=1 ;   $1 && print "$x" ; } ; l false ; (l false) ; l true )' "2"
    # (short)
    testfunc ${LINENO} '(function l { typeset -S -s -i x ;  x+=1 ;   $1 && print "$x" ; } ; l false ; l false   ; l true )' "3"
    testfunc ${LINENO} '(function l { typeset -S -s -i x ;  x+=1 ;   $1 && print "$x" ; } ; l false ; (l false) ; l true )' "2"

    # float
    testfunc ${LINENO} '(function l { float -S x=0.5 ;  (( x+=.5 )) ;   $1 && print "$x" ; } ; l false ; l false   ; l true )' "2"
    testfunc ${LINENO} '(function l { float -S x=0.5 ;  (( x+=.5 )) ;   $1 && print "$x" ; } ; l false ; (l false) ; l true )' "1.5"

    return 0
}

# test2: Test the more complex datatypes
function test2
{
    compound out=( typeset stdout stderr ; integer res )
    integer i

    test_t -r -a tests=(
        (
            name='compound'
            cmd=$'
                function l
                {
                    compound -S s=(
                        integer a=1
                        integer b=2
                    )

                    (( s.a++, s.b++ ))

                    $1 && printf "a=%d, b=%d\n" s.a s.b
                }
                (l false ; l false ; l true ; printf ";")
                (l false ; l false ; l true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )
        (
            name='compound_nameref'
            cmd=$'
                function l_n
                {
                    nameref sn=$2
                    (( sn.a++, sn.b++ ))

                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function l
                {
                    compound -S s=( a=1 b=2 )
                    l_n $1 s
                }
                (l false ; l false ; l true ; printf ";")
                (l false ; l false ; l true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='type'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )
                function l
                {
                    ab_t -S s

                    s.increment

                    $1 && printf "a=%d, b=%d\n" s.a s.b
                }
                (l false ; l false ; l true ; printf ";")
                (l false ; l false ; l true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='type_nameref'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )
                function l_n
                {
                    nameref sn=$2

                    sn.increment

                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function l
                {
                    ab_t -S s
                    l_n $1 s
                }
                (l false ; l false ; l true ; printf ";")
                (l false ; l false ; l true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='indexed_string_array_appendelement'
            cmd=$'
                function ar
                {
                    typeset -a -S s=( "hello" )

                    s+=( "an element" )

                    $1 && { printf "%s" "${s[@]}" ; printf "\n" ; }
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'helloan elementan elementan element\n;helloan elementan elementan element\n;'
        )

        (
            name='indexed_string_array_nameref_appendelement'
            cmd=$'
                function ar_n
                {
                    nameref sn=$2
                    sn+=( "an element" )

                    $1 && { printf "%s" "${sn[@]}" ; printf "\n" ; }
                }
                function ar
                {
                    typeset -a -S s=( "hello" )
                    ar_n $1 s
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'helloan elementan elementan element\n;helloan elementan elementan element\n;'
        )

        (
            name='associative_string_array_appendelement'
            cmd=$'
                function ar
                {
                    typeset -A -S s=( [0]="hello" )

                    s[$(( ${#s[@]} + 1))]="an element"

                    $1 && { printf "%s" "${s[@]}" ; printf "\n" ; }
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'helloan elementan elementan element\n;helloan elementan elementan element\n;'
        )

        (
            name='associative_string_array_nameref_appendelement'
            cmd=$'
                function ar_n
                {
                    nameref sn=$2

                    sn[$(( ${#sn[@]} + 1))]="an element"

                    $1 && { printf "%s" "${sn[@]}" ; printf "\n" ; }
                }
                function ar
                {
                    typeset -A -S s=( [0]="hello" )
                    ar_n $1 s
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'helloan elementan elementan element\n;helloan elementan elementan element\n;'
        )

        (
            name='indexed_compound_array_editelement'
            cmd=$'
                function ar
                {
                    compound -S -a s=(
                        [5]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    (( s[5].a++, s[5].b++ ))
                    $1 && printf "a=%d, b=%d\n" s[5].a s[5].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='indexed_compound_array_nameref_editelement'
            cmd=$'
                function ar_n
                {
                    nameref sn=$2

                    (( sn.a++, sn.b++ ))
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    compound -S -a s=(
                        [5]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    ar_n $1 s[5]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='2d_indexed_compound_array_editelement'
            cmd=$'
                function ar
                {
                    compound -S -a s=(
                        [8][5]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    (( s[8][5].a++, s[8][5].b++ ))
                    $1 && printf "a=%d, b=%d\n" s[8][5].a s[8][5].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='2d_indexed_compound_array_nameref_editelement'
            cmd=$'
                function ar_n
                {
                    nameref sn=$2

                    (( sn.a++, sn.b++ ))
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    compound -S -a s=(
                        [8][5]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    ar_n $1 s[8][5]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='4d_indexed_compound_array_editelement'
            cmd=$'
                function ar
                {
                    compound -S -a s=(
                        [8][5][0][9]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    (( s[8][5][0][9].a++, s[8][5][0][9].b++ ))
                    $1 && printf "a=%d, b=%d\n" s[8][5][0][9].a s[8][5][0][9].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='4d_indexed_compound_array_nameref_editelement'
            cmd=$'
                function ar_n
                {
                    nameref sn=$2

                    (( sn.a++, sn.b++ ))
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    compound -S -a s=(
                        [8][5][0][9]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    ar_n $1 s[8][5][0][9]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='associative_compound_array_editelement'
            cmd=$'
                function ar
                {
                    compound -S -A s=(
                        [5]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    (( s[5].a++, s[5].b++ ))
                    $1 && printf "a=%d, b=%d\n" s[5].a s[5].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='associative_compound_array_nameref_editelement'
            cmd=$'
                function ar_n
                {
                    nameref sn=$2

                    (( sn.a++, sn.b++ ))
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    compound -S -A s=(
                        [5]=(
                            integer a=1
                            integer b=2
                        )
                    )

                    ar_n $1 s[5]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='indexed_type_array_editelement'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )

                function ar
                {
                    ab_t -S -a s
                    [[ -v s[5] ]] || s[5]=( ) # how do I init an array of types ?

                    s[5].increment
                    $1 && printf "a=%d, b=%d\n" s[5].a s[5].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='indexed_type_array_nameref_editelement'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )

                function ar_n
                {
                    nameref sn=$2

                    sn.increment
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    ab_t -S -a s
                    [[ -v s[5] ]] || s[5]=( ) # how do I init an array of types ?

                    ar_n $1 s[5]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='2d_indexed_type_array_editelement'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )

                function ar
                {
                    ab_t -S -a s
                    [[ -v s[9][5] ]] || s[9][5]=( ) # how do I init an array of types ?

                    s[9][5].increment
                    $1 && printf "a=%d, b=%d\n" s[9][5].a s[9][5].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='2d_indexed_type_array_nameref_editelement'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )

                function ar_n
                {
                    nameref sn=$2

                    sn.increment
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    ab_t -S -a s
                    [[ -v s[9][5] ]] || s[9][5]=( ) # how do I init an array of types ?

                    ar_n $1 s[9][5]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='associative_type_array_editelement'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )

                function ar
                {
                    ab_t -S -A s
                    [[ -v s[5] ]] || s[5]=( ) # how do I init an array of types ?

                    s[5].increment
                    $1 && printf "a=%d, b=%d\n" s[5].a s[5].b
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )

        (
            name='associative_type_array_nameref_editelement'
            cmd=$'
                typeset -T ab_t=(
                    integer a=1
                    integer b=2

                    function increment
                    {
                        (( _.a++, _.b++ ))
                    }
                )

                function ar_n
                {
                    nameref sn=$2

                    sn.increment
                    $1 && printf "a=%d, b=%d\n" sn.a sn.b
                }
                function ar
                {
                    ab_t -S -A s
                    [[ -v s[5] ]] || s[5]=( ) # how do I init an array of types ?

                    ar_n $1 s[5]
                }
                (ar false ; ar false ; ar true ; printf ";")
                (ar false ; ar false ; ar true ; printf ";")
            '
            expected_output=$'a=4, b=5\n;a=4, b=5\n;'
        )
    )

    for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do
        nameref currtest=tests[i]

        #print -u2 -- "${currtest.cmd}"
        out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${currtest.cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }"

        (( out.res == 0 )) || log_error "${currtest.name}: Test shell returned with exit code ${out.res}"
        [[ "${out.stdout}" == "${currtest.expected_output}" ]] || log_error "${currtest.name}: Expected stdout == $(printf "%q\n" "${currtest.expected_output}"), got $(printf "%q\n" "${out.stdout}")"
        [[ "${out.stderr}" == '' ]] || log_error "${currtest.name}: Expected empty stderr, got $(printf "%q\n" "${out.stderr}")"
    done

    return 0
}

# Run tests
test1
test2

# Test visibilty of "global" vs. "static" variables. if we have a "static" variable in a
# function and "unset" it we should see a global variable with the same name, right ?
integer hx=5
function test_hx_scope
{
    integer -S hx=9
    $2 && unset hx
    $1 && printf 'hx=%d\n' hx
}

test_hx_scope false false
test_hx_scope false false

# First test the "unset" call in a $(...) subshell...
[[ "$( test_hx_scope true true   )" == 'hx=5' ]] || log_error "can't see global variable hx after unsetting static variable hx"

# ... end then test whether the value has changed.
[[ "${ test_hx_scope true false ;}" == 'hx=9' ]] || log_error "hx variable somehow changed"

out=$(function fun2
{
    nameref sn=$1
    (( sn.a++, sn.b++ ))
    $2 && printf "a=%d, b=%d\n" sn.a sn.b
}

function fun1
{
    compound -S s=( a=0 b=0 )
    fun2 s $1
}

(fun1 false ; fun1 false ; fun1 true)
(fun1 false ; fun1 false ; fun1 true)
)

[[ $out == $'a=3, b=3\na=3, b=3' ]] || log_error 'static variables in functions with initializers not working'
