# (c) 2021 Ashok P. Nadkarni
# See LICENSE for license terms.
#
# Tests for the cffi::type command

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

namespace eval cffi::test {
    proc testalias {id def result {setup ""}} {
        set alias [string map {- _} $id]
        test $id "alias $def" \
            -setup "cffi::alias delete *; $setup" \
            -cleanup "cffi::alias delete *" \
            -body "cffi::alias define $alias {$def}; cffi::type info $alias" \
            -result $result -match dict
    }

    testsubcmd ::cffi::alias
    testnumargs alias-define "cffi::alias define" "(ALIASDEFS | ALIAS DEFINITION)"
    testnumargs alias-define "cffi::alias define" "(ALIASDEFS | ALIAS DEFINITION)"

    test alias-define-error-0 "Invalid alias syntax - starting with numeric" -body {
        cffi::alias define 0alias int
    } -result "Invalid value \"0alias\". $errorMessages(namesyntax)" -returnCodes error

    test alias-define-error-1 "Invalid alias syntax - invalid char" -body {
        cffi::alias define al-ias int
    } -result "Invalid value \"al-ias\". $errorMessages(namesyntax)" -returnCodes error

    test alias-define-error-2 "Invalid alias syntax - array" -body {
        cffi::alias define {xx[2]} int
    } -result "Invalid value \"xx\[2\]\". $errorMessages(namesyntax)" -returnCodes error

    test alias-define-error-3 "Invalid alias - built in type" -body {
        cffi::alias define schar int
    } -result "Type or alias \"schar\" already exists." -returnCodes error

    test alias-define-error-4 "Invalid alias - existing" -setup {
        cffi::alias delete *
        cffi::alias define X int
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias define X short
    } -result "Alias \"X\" already exists. Alias exists with a different definition." -returnCodes error

    test alias-define-error-5 "Invalid alias - nonexisting" -body {
        cffi::alias define X Y
    } -result "Invalid value \"Y\". Invalid base type." -returnCodes error

    testalias alias-define-0 "int" "Size 4 Count -1 Alignment 4 Definition int BaseSize 4"
    testalias alias-define-1 "double\[2\]" "Size 16 Count 2 Alignment 8 Definition {{double\[2\]}} BaseSize 8"
    test alias-define-2 {alias int[n]} -body {
        cffi::alias define alias_define_2 {int[n]}
        cffi::type info alias_define_2 -vlacount 2
    } -cleanup {
        cffi::alias delete *
    } -result [list Size 8 Count n Alignment 4 Definition [list {int[n]}] BaseSize 4]

    testalias alias-define-nested-0 "nested" "Size 4 Count -1 Alignment 4 Definition {int out} BaseSize 4" {
        cffi::alias define nested {int out}
    }

    test alias-define-array-0 "Arrays of aliases" -setup {
        cffi::alias delete *
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias define X int
        cffi::type info {X[2]}
    } -result [list Size 8 Count 2 Alignment 4 Definition {{int[2]}} BaseSize 4]

    test alias-define-multi-0 "Empty defs" -setup {
        cffi::alias delete *
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias define {}
        cffi::alias list
    } -result {}

    test alias-define-multi-1 "Multiple aliases" -setup {
        cffi::alias delete *
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias define {f float i {int nonzero}}
        list [lsort [cffi::alias list]] [cffi::type info f] [cffi::type info i]
    } -result [list [lprefixns f i] {Size 4 Count -1 Alignment 4 Definition float BaseSize 4} {Size 4 Count -1 Alignment 4 Definition {int nonzero} BaseSize 4}]

    test alias-define-multi-error-0 "Missing definition" -setup {
        cffi::alias delete *
    } -cleanup {
        cffi::alias delete *
    } -body {
        list [catch {cffi::alias define {f float i}} result] [cffi::alias list] $result
    } -result {1 {} {Invalid value "f float i". Invalid alias dictionary, missing definition for alias.}}



    test alias-define-nested-1 "Redefine nested" -setup {
        cffi::alias define X int
        cffi::alias define Y X
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias delete X
        cffi::alias define X float
        cffi::alias body Y
    } -result int
    test alias-define-nested-2 "Delete nested" -setup {
        cffi::alias define X int
        cffi::alias define Y X
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias delete X
        cffi::alias body Y
    } -result int

    testalias alias-define-merge-0 "merger nonzero" "Size 4 Count -1 Alignment 4 Definition {int nonzero errno} BaseSize 4" {
        cffi::alias define merger {int errno}
    }

    test alias-define-duplicate-0 "alias define duplicate with same definition" -setup {
        cffi::alias delete *
        cffi::alias define A "int in storeonerror"
    } -body {
        cffi::alias define A "int storeonerror in"
        cffi::alias body A
    } -result {int in storeonerror}

    ###
    # alias body
    testnumargs alias-body "cffi::alias body" "ALIAS"
    test alias-body-0 "alias body" -setup {
        cffi::alias delete *
        cffi::alias define X int
        cffi::alias define Y {X out}
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias body Y
    } -result "int out"

    ###
    # alias list
    testnumargs alias-list "cffi::alias list" "" "?PATTERN?"
    test alias-list-0 "alias empty list" -setup {
        cffi::alias delete *
    } -body {
        cffi::alias list
    } -result {}

    test alias-list-1 "alias list" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -cleanup {
        cffi::alias delete *
    } -body {
        lsort [cffi::alias list]
    } -result [lprefixns abc abd x]

    test alias-list-2 "alias list exact" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias list abc
    } -result [lprefixns abc]

    test alias-list-3 "alias list glob" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -cleanup {
        cffi::alias delete *
    } -body {
        lsort [cffi::alias list ab*]
    } -result [lprefixns abc abd]

    test alias-list-4 "alias list nomatch" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -cleanup {
        cffi::alias delete *
    } -body {
        lsort [cffi::alias list z*]
    } -result {}

    ###
    # alias delete
    testnumargs alias-delete "cffi::alias delete" "PATTERN"
    test alias-delete-0 "alias delete glob" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias delete abd
        lsort [cffi::alias list ab*]
    } -result [lprefixns abc]

    test alias-delete-1 "alias delete glob" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -cleanup {
        cffi::alias delete *
    } -body {
        cffi::alias delete ab*
        lsort [cffi::alias list]
    } -result [lprefixns x]

    test alias-delete-2 "alias delete *" -setup {
        cffi::alias delete *
        cffi::alias define abd int
        cffi::alias define abc float
        cffi::alias define x string
    } -body {
        list [lsort [cffi::alias list]] [cffi::alias delete *] [cffi::alias list]
    } -result [list [lprefixns abc abd x] {} {}]

    test alias-delete-error-0 "alias delete - too many arguments" -body {
        cffi::alias delete X int
    } -result "wrong # args: should be \"cffi::alias delete PATTERN\"" -returnCodes error

    test alias-delete-error-1 "alias delete - too few arguments" -body {
        cffi::alias delete
    } -result "wrong # args: should be \"cffi::alias delete PATTERN\"" -returnCodes error

    ###
    # alias load
    #
    testnumargs alias-load "cffi::alias load" "ALIASSET"
    test alias-load-C-0 "alias load C" -setup {
        reset_aliases
    } -body {
        cffi::alias load C
        list [cffi::alias list] [cffi::alias list ::*] [lsort [cffi::alias list ::cffi::c::*]]
    } -result [list [list] [list ] [lprefixcffic _Bool bool int16_t int32_t int64_t int8_t size_t ssize_t uint16_t uint32_t uint64_t uint8_t]]

    test alias-load-win-0 "alias load win32" -setup {
        reset_aliases
    } -constraints win -body {
        cffi::alias load win32
        list [cffi::alias list] [cffi::alias list ::*] [lsort [cffi::alias list ::cffi::c::*]]
    } -result [list [list] [list ] [lprefixcffic BOOL BOOLEAN BYTE CHAR DWORD DWORDLONG DWORD_PTR HALF_PTR HANDLE INT INT_PTR LONG LONGLONG LONG_PTR LPARAM LPVOID LRESULT SHORT SIZE_T SSIZE_T UCHAR UINT UINT_PTR ULONG ULONGLONG ULONG_PTR USHORT WORD WPARAM]]

    test alias-load-posix-0.a "alias load posix" -setup {
        reset_aliases
    } -constraints win -body {
        cffi::alias load posix
        list [cffi::alias list] [cffi::alias list ::*] [lsort [cffi::alias list ::cffi::c::*]]
    } -result [list [list ] [list ] [lprefixcffic dev_t ino_t off_t time_t]]

    test alias-load-posix-0.b "alias load" -setup {
        reset_aliases
        uplevel #0 cffi::alias delete *
    } -constraints !win -body {
        cffi::alias load posix
        list [cffi::alias list] [cffi::alias list ::*] [lsort [cffi::alias list ::cffi::c::*]]
    } -result [list [list] [list ] [lprefixcffic blkcnt_t blksize_t clock_t dev_t fsblkcnt_t fsfilcnt_t gid_t id_t ino_t key_t mode_t nlink_t off_t pid_t size_t ssize_t suseconds_t time_t uid_t]]

    test alias-load-error-0 "alias load - unknown set" -body {
        cffi::alias load xx
    } -result "Invalid value \"xx\". Unknown predefined alias set." -returnCodes error

    ###
    # Scope tests
    test alias-define-scope-0 "Define aliases in multiple scopes" -setup {
        reset_aliases
    } -body {
        namespace eval ::ns {cffi::alias define INT int}
        namespace eval :: {cffi::alias define INT long}
        cffi::alias define ::ns::ns2::INT longlong
        list \
            [dict get [cffi::type info ::ns::INT] Definition] \
            [dict get [cffi::type info ::INT] Definition] \
            [dict get [cffi::type info ::ns::ns2::INT] Definition]
    } -result {int long longlong}

    test alias-define-scope-1 "Resolve aliases in multiple scopes" -setup {
        reset_aliases
        namespace eval ::ns {cffi::alias define INT int}
        namespace eval :: {cffi::alias define INT long}
        cffi::alias define ::ns::ns2::INT longlong
    } -body {
        list \
            [dict get [namespace eval ::ns {cffi::type info INT}] Definition] \
            [dict get [namespace eval :: {cffi::type info INT}] Definition] \
            [dict get [namespace eval ::ns::ns2 {cffi::type info INT}] Definition]
    } -result {int long longlong}
 
    test alias-list-scope-0 "alias list in scope" -setup {
        reset_aliases
    } -body {
        cffi::alias define INT int
        namespace eval :: {cffi::alias define LONGLONG longlong}
        namespace eval ::ns {cffi::alias define LONG long}
        list \
            [lsort [cffi::alias list]] \
            [lsort [namespace eval ::ns {cffi::alias list}]] \
            [lsort [uplevel #0 cffi::alias list]]
    } -result [list [lprefixns INT] ::ns::LONG ::LONGLONG]

    test alias-body-scope-0 "alias body in scope" -setup {
        reset_aliases
    } -body {
        cffi::alias define INT int
        namespace eval :: {cffi::alias define INT longlong}
        namespace eval ::ns {cffi::alias define INT long}
        list \
            [cffi::alias body INT] \
            [namespace eval ::ns {cffi::alias body INT}] \
            [uplevel #0 cffi::alias body INT]
    } -result {int long longlong}

    test alias-delete-scope-0 "alias delete in scope" -setup {
        reset_aliases
    } -body {
        cffi::alias define INT int
        namespace eval :: {cffi::alias define LONGLONG longlong}
        namespace eval ::ns {cffi::alias define LONG long}
        list \
            [cffi::alias list] \
            [namespace eval ::ns {cffi::alias list}] \
            [uplevel #0 cffi::alias list] \
            [cffi::alias delete *] \
            [cffi::alias list] \
            [namespace eval ::ns {cffi::alias list}] \
            [uplevel #0 cffi::alias list]
    } -result {::cffi::test::INT ::ns::LONG ::LONGLONG {} {} ::ns::LONG ::LONGLONG}

    test alias-clear-0 "Erase all aliases" -setup {
        reset_aliases
        cffi::alias define INT int
        namespace eval :: {cffi::alias define LONGLONG longlong}
        namespace eval ::ns {cffi::alias define LONG long}
    } -body {
        set pre [list [cffi::alias list *] [cffi::alias list ::ns::*] [cffi::alias list ::*]]
        cffi::alias clear
        set post [list [cffi::alias list *] [cffi::alias list ::ns::*] [cffi::alias list ::*]]
        list $pre $post
    } -result {{::cffi::test::INT ::ns::LONG ::LONGLONG} {{} {} {}}}

}
::tcltest::cleanupTests
namespace delete cffi::test
