#---------------------------------------------------------------------
# TITLE:
#       widgetclass.test
#
# AUTHOR:
#       Arnulf Wiedemann with a lot of code form the snit tests by
#       Will Duquette
#
# DESCRIPTION:
#       Test cases for ::itcl::type command.
#       Uses the ::tcltest:: harness.
#
#       There is at least Tcl 8.6a3 needed
#
#    The tests assume tcltest 2.2
#-----------------------------------------------------------------------

# ### ### ### ######### ######### #########
## Declare the minimal version of Tcl required to run the package
## tested by this testsuite, and its dependencies.

proc testsNeedTcl {version} {
    # This command ensures that a minimum version of Tcl is used to
    # run the tests in the calling testsuite. If the minimum is not
    # met by the active interpreter we forcibly bail out of the
    # testsuite calling the command. The command has to be called
    # immediately after loading the utilities.

    if {[package vsatisfies [package provide Tcl] ${version}-]} return

    puts "    Aborting the tests found in \"[file tail [info script]]\""
    puts "    Requiring at least Tcl $version, have [package provide Tcl]."

    # This causes a 'return' in the calling scope.
    return -code return
}

# ### ### ### ######### ######### #########
## Declare the minimum version of Tcltest required to run the
## testsuite.

proc testsNeedTcltest {version} {
    # This command ensure that a minimum version of the Tcltest
    # support package is used to run the tests in the calling
    # testsuite. If the minimum is not met by the loaded package we
    # forcibly bail out of the testsuite calling the command. The
    # command has to be called after loading the utilities. The only
    # command allowed to come before it is 'textNeedTcl' above.

    # Note that this command will try to load a suitable version of
    # Tcltest if the package has not been loaded yet.

    if {[lsearch [namespace children] ::tcltest] == -1} {
	if {![catch {
	    package require tcltest $version
	}]} {
	    namespace import -force ::tcltest::*
	    return
	}
    } elseif {[package vcompare [package present tcltest] $version] >= 0} {
	namespace import -force ::tcltest::*
	return
    }

    puts "    Aborting the tests found in [file tail [info script]]."
    puts "    Requiring at least tcltest $version, have [package present tcltest]"

    # This causes a 'return' in the calling scope.
    return -code return
}

# Set up for Tk tests: enter the event loop long enough to catch
# any bgerrors.
proc tkbide {{msg "tkbide"} {msec 500}} {
    set ::bideVar 0
    set ::bideError ""
    set ::bideErrorInfo ""
    # It looks like update idletasks does the job.
    if {0} {
	after $msec {set ::bideVar 1}
	tkwait variable ::bideVar
    }
    update idletasks
    if {"" != $::bideError} {
	error "$msg: $::bideError" $::bideErrorInfo
    }
}



testsNeedTcl     8.6
testsNeedTcltest 2.2

interp alias {} type {} ::itcl::type
interp alias {} widget {} ::itcl::widget

# Marks tests which are only for Tk.
tcltest::testConstraint tk [expr {![catch {package require Tk}]}]

::tcltest::loadTestedCommands
package require itcl

#-----------------------------------------------------------------------
# Widgets

# A widget is just a widgetadaptor with an automatically created hull
# component (a Tk frame).  So the widgetadaptor tests apply; all we
# need to test here is the frame creation.

test widget-1.1 {creating a widget
} -constraints {
    tk
} -body {
    widget myframe {
	delegate method * to itcl_hull
	delegate option * to itcl_hull
    }

    myframe create .frm -background green

    set a [.frm cget -background]
    set b [.frm itcl_hull]

    destroy .frm
    tkbide
    list $a $b
} -cleanup {
    myframe destroy
} -result {green ::itcl::internal::widgets::hull1.frm}

test widget-2.1 {can't redefine hull
} -constraints {
    tk
} -body {
    # there is no need to define or set itcl_hull as that is done automatically
    widget myframe {
	method resethull {} {
	set itcl_hull ""
	}
    }

    myframe .frm

    .frm resethull
} -returnCodes {
    error
} -cleanup {
    myframe destroy
} -result {can't set "itcl_hull": The itcl_hull component cannot be redefined}


#-----------------------------------------------------------------------
# install
#
# The install command is used to install widget components, while getting
# options for the option database.

test install-1.1 {installed components are created properly
} -constraints {
    tk
} -body {
    widget myframe {
	# Delegate an option just to make sure the component variable
	# exists.
	delegate option -font to text

	constructor {args} {
	    installcomponent text using text $win.text -background green
	}

	method getit {} {
	    $win.text cget -background
	}
    }

    myframe .frm
    set a [.frm getit]
    destroy .frm
    tkbide
    set a
} -cleanup {
    myframe destroy
} -result {green}

test install-1.2 {installed components are saved properly
} -constraints {
    tk
} -body {
    widget myframe {
	# Delegate an option just to make sure the component variable
	# exists.
	delegate option -font to text

	constructor {args} {
	    installcomponent text using text $win.text -background green
	}

	method getit {} {
	    $text cget -background
	}
    }

    myframe .frm
    set a [.frm getit]
    destroy .frm
    tkbide
    set a
} -cleanup {
    myframe destroy
} -result {green}

test install-1.4 {install queries option database
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option -font to text

	typeconstructor {
	    option add *Myframe.font Courier
	}

	constructor {args} {
	    installcomponent text using text $win.text
	}
    }

    myframe .frm
    set a [.frm cget -font]
    destroy .frm
    tkbide
    set a
} -cleanup {
    myframe destroy
} -result {Courier}

test install-1.5 {explicit options override option database
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option -font to text

	typeconstructor {
	    option add *Myframe.font Courier
	}

	constructor {args} {
	    installcomponent text using text $win.text -font Times
	}
    }

    myframe .frm
    set a [.frm cget -font]
    destroy .frm
    tkbide
    set a
} -cleanup {
    myframe destroy
} -result {Times}

test install-1.6 {option db works with targetted options
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option -textfont to text as -font

	typeconstructor {
	    option add *Myframe.textfont Courier
	}

	constructor {args} {
	    installcomponent text using text $win.text
	}
    }

    myframe .frm
    set a [.frm cget -textfont]
    destroy .frm
    tkbide
    set a
} -cleanup {
    myframe destroy
} -result {Courier}

test install-1.8 {install can install non-widget components
} -constraints {
    tk
} -body {
    type dog {
	option -tailcolor black
    }

    widget myframe {
	delegate option -tailcolor to thedog

	typeconstructor {
	    option add *Myframe.tailcolor green
	}

	constructor {args} {
	    installcomponent thedog using dog $win.dog
	}
    }

    myframe .frm
    set a [.frm cget -tailcolor]
    destroy .frm
    tkbide
    set a

} -cleanup {
    dog destroy
    myframe destroy
} -result {green}

test install-1.9 {ok if no options are delegated to component
} -constraints {
    tk
} -body {
    type dog {
	option -tailcolor black
    }

    widget myframe {
	constructor {args} {
	    installcomponent thedog using dog $win.dog
	}
    }

    myframe .frm
    destroy .frm
    tkbide

    # Test passes if no error is raised.
    list ok
} -cleanup {
    myframe destroy
    dog destroy
} -result {ok}

test install-2.1 {
    delegate option * for a non-shadowed option.  The text widget's
    -foreground and -font options should be set according to what's
    in the option database on the widgetclass.
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option * to text

	typeconstructor {
	    option add *Myframe.foreground red
	    option add *Myframe.font {Times 14}
	}

	constructor {args} {
	    installcomponent text using text $win.text
	}
    }

    myframe .frm
    set a [.frm cget -foreground]
    set b [.frm cget -font]
    destroy .frm
    tkbide

    list $a $b
} -cleanup {
    myframe destroy
} -result {red {Times 14}}


test install-2.2 {
    Delegate option * for a shadowed option.  Foreground is declared
    as a non-delegated option, hence it will pick up the option database
    default.  -foreground is not included in the "delegate option *", so
    the text widget's -foreground option will not be set from the
    option database.
} -constraints {
    tk
} -body {
    widget myframe {
	option -foreground white
	delegate option * to text

	typeconstructor {
	    option add *Myframe.foreground red
	}

	constructor {args} {
	    installcomponent text using text $win.text
	}

	method getit {} {
	    $text cget -foreground
	}
    }

    myframe .frm
    set a [.frm cget -foreground]
    set b [.frm getit]
    destroy .frm
    tkbide

    expr {![string equal $a $b]}
} -cleanup {
    myframe destroy
} -result {1}

test install-2.3 {
    Delegate option * for a creation option.  Because the text widget's
    -foreground is set explicitly by the constructor, that always
    overrides the option database.
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option * to text

	typeconstructor {
	    option add *Myframe.foreground red
	}

	constructor {args} {
	    installcomponent text using text $win.text -foreground blue
	}
    }

    myframe .frm
    set a [.frm cget -foreground]
    destroy .frm
    tkbide

    set a
} -cleanup {
    myframe destroy
} -result {blue}

test install-2.4 {
    Delegate option * with an excepted option.  Because the text widget's
    -state is excepted, it won't be set from the option database.
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option * to text except -state

	typeconstructor {
	    option add *Myframe.foreground red
	    option add *Myframe.state disabled
	}

	constructor {args} {
	    installcomponent text using text $win.text
	}

	method getstate {} {
	    $text cget -state
	}
    }

    myframe .frm
    set a [.frm getstate]
    destroy .frm
    tkbide

    set a
} -cleanup {
    myframe destroy
} -result {normal}


#-----------------------------------------------------------------------
# Advanced installhull tests
#
# installhull is used to install the hull widget for both widgets and
# widget adaptors.  It has two forms.  In one form it installs a widget
# created by some third party; in this form no querying of the option
# database is needed, because we haven't taken responsibility for creating
# it.  But in the other form (installhull using) installhull actually
# creates the widget, and takes responsibility for querying the
# option database as needed.
#
# NOTE: "installhull using" is always used to create a widget's hull frame.
#
# That options passed into installhull override those from the
# option database.

test installhull-1.1 {
    options delegated to a widget's itcl_hull frame with the same name are
    initialized from the option database.  Note that there's no
    explicit code in Snit to do this; it happens because we set the
    -class when the widget was created.  In fact, it happens whether
    we delegate the option name or not.
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option -background to itcl_hull

	typeconstructor {
	    option add *Myframe.background red
	    option add *Myframe.width 123
	}

	method getwid {} {
	    $itcl_hull cget -width
	}
    }

    myframe .frm
    set a [.frm cget -background]
    set b [.frm getwid]
    destroy .frm
    tkbide
    list $a $b
} -cleanup {
    myframe destroy
} -result {red 123}

test installhull-1.2 {
    Options delegated to a widget's itcl_hull frame with a different name are
    initialized from the option database.
} -constraints {
    tk
} -body {
    widget myframe {
	delegate option -mainbackground to itcl_hull as -background

	typeconstructor {
	    option add *Myframe.mainbackground green
	}
    }

    myframe .frm
    set a [.frm cget -mainbackground]
    destroy .frm
    tkbide
    set a
} -cleanup {
    myframe destroy
} -result {green}



test option-5.1 {local widget options read from option database
} -constraints {
    tk
} -body {
    widget dog {
	option -foo a
	option -bar b

	typeconstructor {
	    option add *Dog.bar bb
	}
    }

    dog .fido
    set a [.fido cget -foo]
    set b [.fido cget -bar]
    destroy .fido
    tkbide

    list $a $b

} -cleanup {
    dog destroy
} -result {a bb}

test option-5.2 {local option database values available in constructor
} -constraints {
    tk
} -body {
    widget dog {
	option -bar b
	variable saveit

	typeconstructor {
	    option add *Dog.bar bb
	}

	constructor {args} {
	    set saveit $itcl_options(-bar)
	}

	method getit {} {
	    return $saveit
	}
    }

    dog .fido
    set result [.fido getit]
    destroy .fido
    tkbide

    set result
} -cleanup {
    dog destroy
} -result {bb}

#-----------------------------------------------------------------------
# Setting the widget class explicitly

test widgetclass-1.3 {widgetclass must begin with uppercase letter
} -constraints {
    tk
} -body {
    widget dog {
	widgetclass dog
    }
} -returnCodes {
    error
} -result {widgetclass "dog" does not begin with an uppercase letter}

test widgetclass-1.4 {widgetclass can only be defined once
} -constraints {
    tk
} -body {
    widget dog {
	widgetclass Dog
	widgetclass Dog
    }
} -returnCodes {
    error
} -result {too many widgetclass statements}

test widgetclass-1.5 {widgetclass set successfully
} -constraints {
    tk
} -body {
    widget dog {
	widgetclass DogWidget
    }

    # The test passes if no error is thrown.
    list ok
} -cleanup {
    dog destroy
} -result {ok}

test widgetclass-1.6 {implicit widgetclass applied to hull
} -constraints {
    tk
} -body {
    widget dog {
	typeconstructor {
	    option add *Dog.background green
	}

	method background {} {
	    $itcl_hull cget -background
	}
    }

    dog .dog

    set bg [.dog background]

    destroy .dog

    set bg
} -cleanup {
    dog destroy
} -result {green}

test widgetclass-1.7 {explicit widgetclass applied to hull
} -constraints {
    tk
} -body {
    widget dog {
	widgetclass DogWidget

	typeconstructor {
	    option add *DogWidget.background yellow
	}

	method background {} {
	    $itcl_hull cget -background
	}
    }

    dog .dog

    set bg [.dog background]

    destroy .dog

    set bg
} -cleanup {
    dog destroy
} -result {yellow}

#-----------------------------------------------------------------------
# hulltype statement

test hulltype-1.3 {hulltype can be frame
} -constraints {
    tk
} -body {
    widget dog {
	delegate option * to itcl_hull
	hulltype frame
    }

    dog .fido
    catch {.fido configure -use} result
    destroy .fido
    tkbide

    set result
} -cleanup {
    dog destroy
} -result {unknown option "-use"}

test hulltype-1.4 {hulltype can be toplevel
} -constraints {
    tk
} -body {
    widget dog {
	delegate option * to itcl_hull
	hulltype toplevel
    }

    dog .fido
    catch {.fido configure -use} result
    destroy .fido
    tkbide

    set result
} -cleanup {
    dog destroy
} -result {-use use Use {} {}}

test hulltype-1.5 {hulltype can only be defined once
} -constraints {
    tk
} -body {
    widget dog {
	hulltype frame
	hulltype toplevel
    }
} -returnCodes {
    error
} -result {too many hulltype statements}

test hulltype-2.1 {list of valid hulltypes
} -constraints {
    tk
} -body {
    type dog {
    }

    lsort [dog info hulltypes]
} -cleanup {
    dog destroy
} -result {frame labelframe toplevel ttk:frame ttk:labelframe ttk:toplevel}

test winfo-10.1 {widget info widgets
} -constraints {
    tk
} -body {
    widget dog {
    }

    widget cat {
    }

    lsort [dog info widgets]
} -cleanup {
    dog destroy
    cat destroy
} -result {cat dog}

test winfo-10.2 {widget info components
} -constraints {
    tk
} -body {
    widget dog {
	component comp1
	component comp2
    }

    widget cat {
	component comp1
	component comp1a
    }

    set a [lsort [dog info components]]
    set b [lsort [cat info components]]
    list $a $b
} -cleanup {
    dog destroy
    cat destroy
} -result {{comp1 comp2 itcl_hull} {comp1 comp1a itcl_hull}}

test winfo-10.3 {widget info widgetclasses
} -constraints {
    tk
} -body {
    widget dog {
	widgetclass DogWidget
    }

    widget cat {
	widgetclass CatWidget
    }

    lsort [dog info widgetclasses]
} -cleanup {
    dog destroy
    cat destroy
} -result {CatWidget DogWidget}


#---------------------------------------------------------------------
# Clean up

::tcltest::cleanupTests
return
