# do.test --
#
#         Tests for [control::do]
# 
# RCS: @(#) $Id: do.test,v 1.14 2009/11/24 04:52:49 andreas_kupries Exp $
#

# -------------------------------------------------------------------------

source [file join \
	[file dirname [file dirname [file join [pwd] [info script]]]] \
	devtools testutilities.tcl]

testsNeedTcl     8.2
testsNeedTcltest 1.0

testing {
    useLocal control.tcl control
}

# -------------------------------------------------------------------------

namespace import ::control::do

# ----------------------------------------
test {do-1.0} {do ... while} {
    set x 0
    do {incr x} while {$x < 10}
    set x
} 10

# ----------------------------------------
test {do-1.1} {do ... until} {
    set x 0
    do {incr x} until {$x > 10}
    set x
} 11

# ----------------------------------------
test {do-1.2} {break} {
    set x 0
    do {
	incr x
	if {$x == 5} {break}
    } until {$x == 10}
    set x
} 5

# ----------------------------------------
test {do-1.3} {continue} {

    set x 0
    set xx [list]
    do {
	incr x
	if {$x == 5} {continue}
	lappend xx $x
    } until {$x == 10}
    set xx
} {1 2 3 4 6 7 8 9 10}

# ----------------------------------------
test {do-1.4} {error} {
    catch {
	set x 0
	do {
	    incr x
	    if {$x == 5} {foo}
	} while {$x < 10}
    } result
    list $x $result
} {5 {invalid command name "foo"}}

# ----------------------------------------
test {do-1.5} {return} {
    proc foo {} {
	set x 0
	do {
	    incr x
	    if {$x == 5} { return $x }
	} while {$x < 10}
    }
    set result [foo]
    rename foo ""
    set result
} 5

# ----------------------------------------
test {do-1.6} {break in the first loop} {
    set x 0
    do {
	break
	incr x
    } while {$x < 10}
    set x
} 0

# ----------------------------------------
test {do-1.7} {continue in the first loop} {
    set x 0
    set xx [list]
    do {
	incr x
	if {$x == 1} {continue}
	lappend xx $x
    } until {$x == 10}
    set xx
} {2 3 4 5 6 7 8 9 10}

# ----------------------------------------
test {do-1.8} {error in the first loop} {
    set x 0
    catch {
	do {
	    foo
	    incr x
	} until {$x == 10}
    } result
    list $x $result
} {0 {invalid command name "foo"}}

# ----------------------------------------
test {do-1.9} {[do ... while] with false condition} {
    set x 0
    do {
	incr x
    } while 0
    set x
} 1

# ----------------------------------------
test do-1.10 {[do ... until] with true condition} {
    set x 0
    do {
	incr x
    } until 1
    set x
} 1

# ----------------------------------------
test do-1.11 {third arg is neither while nor until} {
    set x 0
    catch {
	do {
	    incr x
	} foo 1
	set x
    } result
    list $x $result
} {0 {bad option "foo": must be until, or while}}

# ----------------------------------------
test do-1.12 {stack traces for errors in the first iteration} {
    proc a {} b
    proc b {} {do c while 1}
    proc c {} d
    catch a
    set ::errorInfo
} {invalid command name "d"
    while executing
"d"
    (procedure "c" line 1)
    invoked from within
"c"
    ("do" body line 1)
    invoked from within
"do c while 1"
    (procedure "b" line 1)
    invoked from within
"b"
    (procedure "a" line 1)
    invoked from within
"a"}

# ----------------------------------------
test do-1.14 {stack traces for errors in subsequent iterations} tcl8.3plus {
    proc a {} b
    proc b {} {
	set i 10
	do {
	    incr i -1
	    c $i
	} while {$i}
    }
    proc c {i} {if {$i==5} e}
    catch a
    set ::errorInfo
} {invalid command name "e"
    while executing
"e"
    (procedure "c" line 1)
    invoked from within
"c $i"
    ("do" body line 3)
    invoked from within
"do {
	    incr i -1
	    c $i
	} while {$i}"
    (procedure "b" line 3)
    invoked from within
"b"
    (procedure "a" line 1)
    invoked from within
"a"}

# ----------------------------------------
test do-2.0 {one-shot do} {
    set x 0
    do {incr x}
    set x
} 1

# ----------------------------------------
test do-2.1 {one-shot do with break} {
    set x 0
    do {incr x; break; incr x}
    set x
} 1

# ----------------------------------------
test do-2.2 {wrong no of arguments} {
    set x 0
    set res [catch {do {incr x} foo} ret]
    list $x $res $errorInfo
} {0 1 {wrong # args: should be "::control::do body" or "::control::do body [until|while] test"
    while executing
"do {incr x} foo"}}

# ----------------------------------------

if {[package vsatisfies [package provide Tcl] 8.6]} {
    # 8.6+
    set do23res {1 {wrong # args: should be "do body ?arg ...?"
    while executing
"do"}}
} elseif {[package vsatisfies [package provide Tcl] 8.5]} {
    # 8.5+
    set do23res {1 {wrong # args: should be "do body ..."
    while executing
"do"}}
} else {
    # 8.4-
    set do23res {1 {wrong # args: should be "do body args"
    while executing
"do"}}
}

test do-2.3 {wrong no of arguments} {} {
    set res [catch do]
    if {[string match \
	     {no value given for parameter "body" to "do"*} \
	     $::errorInfo]
    } then {
	set ::errorInfo {wrong # args: should be "do body args"
    while executing
"do"}
    }
    list $res $::errorInfo
} $do23res

# ----------------------------------------
test do-2.4 {one-shot do with error} {
    set x 0
    set res [catch {do {
	incr x
	foo
	incr x
    }}]
    list $x $res $::errorInfo
} {1 1 {invalid command name "foo"
    while executing
"foo"
    ("do" body line 3)
    invoked from within
"do {
	incr x
	foo
	incr x
    }"}}

testsuiteCleanup

if {[info exists ::argv0] && $::argv0 == [info script]} {
    # a proc that wastes some time
    proc something {n} {
	for {set i 0} {$i < $n} {incr i} {}
    }
    
    proc main {} {
	# run it for the first time to get it byte compiled
	something 1
	
	set payload {
	    something 10
	    incr x
	}
	puts "\nComparing performance of do-while, do-until and builtin while..."
	set format "%-8s : %20s for %4d iteration(s)."
	foreach c {1 10 5000} {
	    puts ""
	    foreach {descr script} {
		{do while} {do $payload while {$x < $c}}
		{do until} {do $payload until {$x == $c}}
		{while} {while {$x < $c} $payload}
	    } {
		set x 0
		puts [format $format $descr [lrange [time $script 1] 0 1] $x]
	    }
	}
    }
    main
}

# Local variables:
# mode: tcl
# End:
