# (c) 2022-2023 Ashok P. Nadkarni
# See LICENSE for license terms.
#
# This file contains tests for cffi::Wrapper pertaining to definition and
# invocation of functions.

source [file join [file dirname [info script]] common.tcl]


# Varargs function definition tests

namespace eval ${NS}::test {
    variable invalidVarargTypes {uchar schar short ushort float}

    testDll function formatVarargs int {buf {chars[n] out} n int fmt string ...}

    test varargs-error-0 {formatVarargs no fixed params} -body {
        testDll function {formatVarargs xxx} int {...}
    } -returnCodes error -result {Invalid value. No fixed parameters present in varargs function definition. Error defining function xxx.}

    test varargs-error-1 {formatVarargs single element vararg} -body {
        formatVarargs buf 100 %s singleelementarg
    } -returnCodes error -result {Invalid value "singleelementarg". A vararg must be a type and value pair.}
    test varargs-error-2 {formatVarargs three element vararg} -body {
        formatVarargs buf 100 %d {int 42 x}
    } -returnCodes error -result {Invalid value "int 42 x". A vararg must be a type and value pair.}
    test varargs-error-3 {formatVarargs bad type} -body {
        formatVarargs buf 100 %s {nosuchtype 42}
    } -returnCodes error -result {Invalid value "nosuchtype". Invalid base type.}
    test varargs-error-4 {formatVarargs output var} -body {
        formatVarargs buf 100 %s {{int out} 42}
    } -returnCodes error -result {Invalid value "int out". Only input parameters permitted for varargs.}
    test varargs-error-5 {formatVarargs inout var} -body {
        formatVarargs buf 100 %s {{int inout} 42}
    } -returnCodes error -result {Invalid value "int inout". Only input parameters permitted for varargs.}
    test varargs-error-6 {formatVarargs retval var} -body {
        formatVarargs buf 100 %s {{int retval} 42}
    } -returnCodes error -result {Invalid value "int retval". Only input parameters permitted for varargs.}
    test varargs-error-7.0 {varargs - stdcall} -body {
        testDll stdcall {formatVarargs xxx} int {buf {chars[n] out} n int fmt string ...}
    } -constraints win32 -returnCodes error -result {Varargs not supported for this calling convention. Error defining function xxx.}

    foreach type $intTypes {
        if {$type in $invalidVarargTypes} {
            test varargs-$type-0 "formatVarargs invalid type $type" -body {
                formatVarargs buf 100 %d [list $type 42]
            } -returnCodes error -result "Invalid value \"$type\". Type not permitted for varargs."
        } else {
            test vararg-$type-0 "formatVarargs type $type" -body {
                formatVarargs buf 100 %d [list $type 42]
                set buf
            } -result 42
            test vararg-$type-error-0 "formatVarargs type $type bad value" -body {
                formatVarargs buf 100 %d [list $type x]
                set buf
            } -result {expected integer but got "x"} -returnCodes error
        }
    }
    foreach type $realTypes {
        if {$type in $invalidVarargTypes} {
            test varargs-$type-0 "formatVarargs invalid type $type" -body {
                formatVarargs buf 100 %g [list $type 42]
            } -returnCodes error -result "Invalid value \"$type\". Type not permitted for varargs."
        } else {
            test vararg-$type-0 "formatVarargs type $type" -body {
                formatVarargs buf 100 %g [list $type 42.5]
                set buf
            } -result 42.5
            test vararg-$type-error-0 "formatVarargs type $type bad value" -body {
                formatVarargs buf 100 %g [list $type x]
                set buf
            } -result {expected floating-point number but got "x"} -returnCodes error
        }
    }

    test vararg-string-0 "formatVarargs type string" -body {
        formatVarargs buf 100 %s {string Hello}
        set buf
    } -result Hello
    test vararg-chars-0 "formatVarargs type chars[]" -body {
        formatVarargs buf 100 %s {chars[10] Hello}
        set buf
    } -result Hello

    test vararg-stdcall-0 "formatVarargs type string" -constraints {notwin32} -body {
        testDll function {formatVarargs stdcallVarargs} int {buf {chars[n] out} n int fmt string ...}
        stdcallVarargs buf 100 %s {string Hello}
        set buf
    } -cleanup {
        rename stdcallVarargs {}
    } -result Hello

    #
    # Varargs for prototypes
    test vararg-prototype-0 "prototype with varargs" -setup {
        cffi::prototype clear
        set addr [testDll addressof formatVarargs]
    } -cleanup {
        cffi::prototype clear
    } -body {
        cffi::prototype function varargsProto int {buf {chars[bufSize] out} bufSize int fmt string ...}
        list [cffi::call [scoped_ptr $addr varargsProto] buf 100 "The %s is %d." {string answer} {int 42}] $buf
    } -result {17 {The answer is 42.}}

    #
    # Varargs for callbacks
    test vararg-prototype-0 "prototype with varargs" -setup {
        cffi::prototype clear
        set addr [testDll addressof formatVarargs]
    } -cleanup {
        cffi::prototype clear
    } -body {
        cffi::prototype function varargsProto int {buf {chars[bufSize] out} bufSize int fmt string ...}
        set fnptr [cffi::callback new [namespace current]::varargsProto my_callback -1]
    } -result {Callbacks cannot have a variable number of parameters.} -returnCodes error
}

${NS}::test::testDll destroy

::tcltest::cleanupTests
namespace delete ${NS}::test
