From 8c05c20df453400c69cdcddd621fd9fc7e692e73 Mon Sep 17 00:00:00 2001 From: pspjuth Date: Mon, 22 Oct 2018 05:56:37 +0000 Subject: Implement TIP 522, Test error codes with Tcltest --- library/tcltest/tcltest.tcl | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/library/tcltest/tcltest.tcl b/library/tcltest/tcltest.tcl index f1b6082..a4954e7 100644 --- a/library/tcltest/tcltest.tcl +++ b/library/tcltest/tcltest.tcl @@ -1841,6 +1841,9 @@ proc tcltest::SubstArguments {argList} { # is optional; default is {}. # returnCodes - Expected return codes. This attribute is # optional; default is {0 2}. +# errorCode - Expected error code. This attribute is +# optional; default is {}. It is a glob pattern. +# If given, returnCodes defaults to {1}. # setup - Code to run before $script (above). This # attribute is optional; default is {}. # cleanup - Code to run after $script (above). This @@ -1882,7 +1885,7 @@ proc tcltest::test {name description args} { # Pre-define everything to null except output and errorOutput. We # determine whether or not to trap output based on whether or not # these variables (output & errorOutput) are defined. - lassign {} constraints setup cleanup body result returnCodes match + lassign {} constraints setup cleanup body result returnCodes errorCode match # Set the default match mode set match exact @@ -1901,7 +1904,7 @@ proc tcltest::test {name description args} { set testAttributes($element) $value } foreach item {constraints match setup body cleanup \ - result returnCodes output errorOutput} { + result returnCodes errorCode output errorOutput} { if {[info exists testAttributes(-$item)]} { set testAttributes(-$item) [uplevel 1 \ ::concat $testAttributes(-$item)] @@ -1912,7 +1915,7 @@ proc tcltest::test {name description args} { } set validFlags {-setup -cleanup -body -result -returnCodes \ - -match -output -errorOutput -constraints} + -errorCode -match -output -errorOutput -constraints} foreach flag [array names testAttributes] { if {$flag ni $validFlags} { @@ -1944,6 +1947,10 @@ proc tcltest::test {name description args} { foreach {strcode numcode} {ok 0 normal 0 error 1 return 2 break 3 continue 4} { set returnCodes [string map -nocase [list $strcode $numcode] $returnCodes] } + # errorCode without returnCode 1 is meaningless + if {$errorCode ne "" && 1 ni $returnCodes} { + set returnCodes 1 + } } else { # This is parsing for the old test command format; it is here # for backward compatibility. @@ -1976,7 +1983,7 @@ proc tcltest::test {name description args} { set code [catch {uplevel 1 $setup} setupMsg] if {$code == 1} { set errorInfo(setup) $::errorInfo - set errorCode(setup) $::errorCode + set errorCodeRes(setup) $::errorCode } set setupFailure [expr {$code != 0}] @@ -2003,7 +2010,7 @@ proc tcltest::test {name description args} { lassign $testResult actualAnswer returnCode if {$returnCode == 1} { set errorInfo(body) $::errorInfo - set errorCode(body) $::errorCode + set errorCodeRes(body) $::errorCode } } @@ -2012,6 +2019,13 @@ proc tcltest::test {name description args} { if {!$setupFailure && ($returnCode ni $returnCodes)} { set codeFailure 1 } + set errCodeFailure 0 + if {!$setupFailure && !$codeFailure && $returnCode == 1 && \ + $errorCode ne "" && \ + ![string match $errorCode $errorCodeRes(body)]} { + set codeFailure 1 + set errCodeFailure 1 + } # If expected output/error strings exist, we have to compare # them. If the comparison fails, then so did the test. @@ -2055,7 +2069,7 @@ proc tcltest::test {name description args} { set code [catch {uplevel 1 $cleanup} cleanupMsg] if {$code == 1} { set errorInfo(cleanup) $::errorInfo - set errorCode(cleanup) $::errorCode + set errorCodeRes(cleanup) $::errorCode } set cleanupFailure [expr {$code != 0}] @@ -2159,7 +2173,7 @@ proc tcltest::test {name description args} { failed:\n$setupMsg" if {[info exists errorInfo(setup)]} { puts [outputChannel] "---- errorInfo(setup): $errorInfo(setup)" - puts [outputChannel] "---- errorCode(setup): $errorCode(setup)" + puts [outputChannel] "---- errorCode(setup): $errorCodeRes(setup)" } } if {$scriptFailure} { @@ -2171,7 +2185,11 @@ proc tcltest::test {name description args} { ($match matching):\n$result" } } - if {$codeFailure} { + if {$errCodeFailure} { + # TODO + puts [outputChannel] "---- Error code was: '$errorCodeRes(body)'" + puts [outputChannel] "---- Error code should have been: '$errorCode'" + } elseif {$codeFailure} { switch -- $returnCode { 0 { set msg "Test completed normally" } 1 { set msg "Test generated error" } @@ -2186,7 +2204,7 @@ proc tcltest::test {name description args} { if {[IsVerbose error]} { if {[info exists errorInfo(body)] && (1 ni $returnCodes)} { puts [outputChannel] "---- errorInfo: $errorInfo(body)" - puts [outputChannel] "---- errorCode: $errorCode(body)" + puts [outputChannel] "---- errorCode: $errorCodeRes(body)" } } } @@ -2212,7 +2230,7 @@ proc tcltest::test {name description args} { puts [outputChannel] "---- Test cleanup failed:\n$cleanupMsg" if {[info exists errorInfo(cleanup)]} { puts [outputChannel] "---- errorInfo(cleanup): $errorInfo(cleanup)" - puts [outputChannel] "---- errorCode(cleanup): $errorCode(cleanup)" + puts [outputChannel] "---- errorCode(cleanup): $errorCodeRes(cleanup)" } } if {$coreFailure} { -- cgit v0.12 From 5b1614f31a3c2c510c726089b6ca499cfb792e72 Mon Sep 17 00:00:00 2001 From: pspjuth Date: Mon, 22 Oct 2018 20:27:03 +0000 Subject: Documentation and revision bump for tip 522 --- doc/tcltest.n | 14 ++++++++++++-- library/init.tcl | 2 +- library/tcltest/pkgIndex.tcl | 2 +- library/tcltest/tcltest.tcl | 10 ++++++---- unix/Makefile.in | 4 ++-- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/doc/tcltest.n b/doc/tcltest.n index 05c1922..b161a2b 100644 --- a/doc/tcltest.n +++ b/doc/tcltest.n @@ -8,7 +8,7 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -.TH "tcltest" n 2.3 tcltest "Tcl Bundled Packages" +.TH "tcltest" n 2.5 tcltest "Tcl Bundled Packages" .so man.macros .BS '\" Note: do not modify the .SH NAME line immediately below! @@ -16,7 +16,7 @@ tcltest \- Test harness support code and utilities .SH SYNOPSIS .nf -\fBpackage require tcltest\fR ?\fB2.3\fR? +\fBpackage require tcltest\fR ?\fB2.5\fR? \fBtcltest::test \fIname description\fR ?\fI\-option value ...\fR? \fBtcltest::test \fIname description\fR ?\fIconstraints\fR? \fIbody result\fR @@ -454,6 +454,7 @@ The valid options for \fBtest\fR are summarized: ?\fB\-output \fIexpectedOutput\fR? ?\fB\-errorOutput \fIexpectedError\fR? ?\fB\-returnCodes \fIcodeList\fR? + ?\fB\-errorCode \fIexpectedErrorCode\fR? ?\fB\-match \fImode\fR? .CE .PP @@ -577,6 +578,15 @@ return codes known to \fBreturn\fR, in both numeric and symbolic form, including extended return codes, are acceptable elements in the \fIexpectedCodeList\fR. Default value is .QW "\fBok return\fR" . +.TP +\fB\-errorCode \fIexpectedErrorCode\fR +. +The optional \fB\-errorCode\fR attribute supplies \fIexpectedErrorCode\fR, +a glob pattern that should match the error code reported from evaluation of the +\fB\-body\fR script. If evaluation of the \fB\-body\fR script returns +a code not matching \fIexpectedErrorCode\fR, the test fails. Default value is +.QW "\fB*\fR" . +If \fB\-returnCodes\fR does not include \fBerror\fR it is set to \fBerror\fR. .PP To pass, a test must successfully evaluate its \fB\-setup\fR, \fB\-body\fR, and \fB\-cleanup\fR scripts. The return code of the \fB\-body\fR script and diff --git a/library/init.tcl b/library/init.tcl index 51339d0..1221e61 100644 --- a/library/init.tcl +++ b/library/init.tcl @@ -810,7 +810,7 @@ foreach {safe package version file} { 1 opt 0.4.7 {opt optparse.tcl} 0 platform 1.0.14 {platform platform.tcl} 0 platform::shell 1.1.4 {platform shell.tcl} - 1 tcltest 2.4.1 {tcltest tcltest.tcl} + 1 tcltest 2.5.0 {tcltest tcltest.tcl} } { if {$isafe && !$safe} continue package ifneeded $package $version [list source [file join $dir {*}$file]] diff --git a/library/tcltest/pkgIndex.tcl b/library/tcltest/pkgIndex.tcl index eadb1bd..fde3ffe 100644 --- a/library/tcltest/pkgIndex.tcl +++ b/library/tcltest/pkgIndex.tcl @@ -9,4 +9,4 @@ # full path name of this file's directory. if {![package vsatisfies [package provide Tcl] 8.5-]} {return} -package ifneeded tcltest 2.4.1 [list source [file join $dir tcltest.tcl]] +package ifneeded tcltest 2.5.0 [list source [file join $dir tcltest.tcl]] diff --git a/library/tcltest/tcltest.tcl b/library/tcltest/tcltest.tcl index a4954e7..8b14142 100644 --- a/library/tcltest/tcltest.tcl +++ b/library/tcltest/tcltest.tcl @@ -22,7 +22,7 @@ namespace eval tcltest { # When the version number changes, be sure to update the pkgIndex.tcl file, # and the install directory in the Makefiles. When the minor version # changes (new feature) be sure to update the man page as well. - variable Version 2.4.1 + variable Version 2.5.0 # Compatibility support for dumb variables defined in tcltest 1 # Do not use these. Call [package provide Tcl] and [info patchlevel] @@ -1842,7 +1842,7 @@ proc tcltest::SubstArguments {argList} { # returnCodes - Expected return codes. This attribute is # optional; default is {0 2}. # errorCode - Expected error code. This attribute is -# optional; default is {}. It is a glob pattern. +# optional; default is {*}. It is a glob pattern. # If given, returnCodes defaults to {1}. # setup - Code to run before $script (above). This # attribute is optional; default is {}. @@ -1895,6 +1895,9 @@ proc tcltest::test {name description args} { # 'return' being used in the test script). set returnCodes [list 0 2] + # Set the default error code pattern + set errorCode "*" + # The old test format can't have a 3rd argument (constraints or # script) that starts with '-'. if {[string match -* [lindex $args 0]] || ([llength $args] <= 1)} { @@ -1948,7 +1951,7 @@ proc tcltest::test {name description args} { set returnCodes [string map -nocase [list $strcode $numcode] $returnCodes] } # errorCode without returnCode 1 is meaningless - if {$errorCode ne "" && 1 ni $returnCodes} { + if {$errorCode ne "*" && 1 ni $returnCodes} { set returnCodes 1 } } else { @@ -2021,7 +2024,6 @@ proc tcltest::test {name description args} { } set errCodeFailure 0 if {!$setupFailure && !$codeFailure && $returnCode == 1 && \ - $errorCode ne "" && \ ![string match $errorCode $errorCodeRes(body)]} { set codeFailure 1 set errCodeFailure 1 diff --git a/unix/Makefile.in b/unix/Makefile.in index 487ae61..c42abbf 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -933,9 +933,9 @@ install-libraries: libraries @echo "Installing package msgcat 1.7.0 as a Tcl Module" @$(INSTALL_DATA) $(TOP_DIR)/library/msgcat/msgcat.tcl \ "$(MODULE_INSTALL_DIR)"/tcl8/8.7/msgcat-1.7.0.tm - @echo "Installing package tcltest 2.4.1 as a Tcl Module" + @echo "Installing package tcltest 2.5.0 as a Tcl Module" @$(INSTALL_DATA) $(TOP_DIR)/library/tcltest/tcltest.tcl \ - "$(MODULE_INSTALL_DIR)"/tcl8/8.5/tcltest-2.4.1.tm + "$(MODULE_INSTALL_DIR)"/tcl8/8.5/tcltest-2.5.0.tm @echo "Installing package platform 1.0.14 as a Tcl Module" @$(INSTALL_DATA) $(TOP_DIR)/library/platform/platform.tcl \ "$(MODULE_INSTALL_DIR)"/tcl8/8.4/platform-1.0.14.tm -- cgit v0.12 From 394e22ef5d4e279dbb1e8d2d6616c84e70f5032f Mon Sep 17 00:00:00 2001 From: pspjuth Date: Mon, 22 Oct 2018 21:15:00 +0000 Subject: Updated tests to exercise new feature. --- tests/assemble.test | 11 ++++++----- tests/dict.test | 12 ++---------- tests/ioCmd.test | 4 ++-- tests/source.test | 7 +++---- tests/tcltest.test | 6 +++--- 5 files changed, 16 insertions(+), 24 deletions(-) diff --git a/tests/assemble.test b/tests/assemble.test index d7c47a9..bea780a 100644 --- a/tests/assemble.test +++ b/tests/assemble.test @@ -852,10 +852,11 @@ test assemble-8.5 {bad context} { -body { namespace eval assem { set x 1 - list [catch {assemble {load x}} result opts] $result [dict get $opts -errorcode] + assemble {load x} } } - -result {1 {cannot use this instruction to create a variable in a non-proc context} {TCL ASSEM LVT}} + -result {cannot use this instruction to create a variable in a non-proc context} + -errorCode {TCL ASSEM LVT} -cleanup {namespace delete assem} } test assemble-8.6 {load1} { @@ -1110,10 +1111,10 @@ test assemble-9.6 {concat} { } test assemble-9.7 {concat} { -body { - list [catch {assemble {concat 0}} result] $result $::errorCode + assemble {concat 0} } - -result {1 {operand must be positive} {TCL ASSEM POSITIVE}} - -cleanup {unset result} + -result {operand must be positive} + -errorCode {TCL ASSEM POSITIVE} } # assemble-10 -- eval and expr diff --git a/tests/dict.test b/tests/dict.test index a6b0cb4..1479727 100644 --- a/tests/dict.test +++ b/tests/dict.test @@ -175,11 +175,7 @@ test dict-4.12 {dict replace command: canonicality is forced} { } {a e c d} test dict-4.13 {dict replace command: type check is mandatory} -body { dict replace { a b c d e } -} -returnCodes error -result {missing value to go with key} -test dict-4.13a {dict replace command: type check is mandatory} { - catch {dict replace { a b c d e }} -> opt - dict get $opt -errorcode -} {TCL VALUE DICTIONARY} +} -errorCode {TCL VALUE DICTIONARY} -result {missing value to go with key} test dict-4.14 {dict replace command: type check is mandatory} -body { dict replace { a b {}c d } } -returnCodes error -result {dict element in braces followed by "c" instead of space} @@ -203,11 +199,7 @@ test dict-4.16a {dict replace command: type check is mandatory} { } {TCL VALUE DICTIONARY QUOTE} test dict-4.17 {dict replace command: type check is mandatory} -body { dict replace " a b \{c d " -} -returnCodes error -result {unmatched open brace in dict} -test dict-4.17a {dict replace command: type check is mandatory} { - catch {dict replace " a b \{c d "} -> opt - dict get $opt -errorcode -} {TCL VALUE DICTIONARY BRACE} +} -errorCode {TCL VALUE DICTIONARY BRACE} -result {unmatched open brace in dict} test dict-4.18 {dict replace command: canonicality forcing doesn't leak} { set example { a b c d } list $example [dict replace $example] diff --git a/tests/ioCmd.test b/tests/ioCmd.test index 948671e..3c4caa7 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -154,10 +154,10 @@ test iocmd-4.11 {read command} { test iocmd-4.12 {read command} -setup { set f [open $path(test1)] } -body { - list [catch {read $f 12z} msg] $msg $::errorCode + read $f 12z } -cleanup { close $f -} -result {1 {expected non-negative integer but got "12z"} {TCL VALUE NUMBER}} +} -result {expected non-negative integer but got "12z"} -errorCode {TCL VALUE NUMBER} test iocmd-5.1 {seek command} -returnCodes error -body { seek diff --git a/tests/source.test b/tests/source.test index 0235bd1..ab6b447 100644 --- a/tests/source.test +++ b/tests/source.test @@ -103,10 +103,9 @@ test source-2.6 {source error conditions} -setup { set sourcefile [makeFile {} _non_existent_] removeFile _non_existent_ } -body { - list [catch {source $sourcefile} msg] $msg $::errorCode -} -match listGlob -result [list 1 \ - {couldn't read file "*_non_existent_": no such file or directory} \ - {POSIX ENOENT {no such file or directory}}] + source $sourcefile +} -match glob -result {couldn't read file "*_non_existent_": no such file or directory} \ + -errorCode {POSIX ENOENT {no such file or directory}} test source-2.7 {utf-8 with BOM} -setup { set sourcefile [makeFile {} source.file] } -body { diff --git a/tests/tcltest.test b/tests/tcltest.test index 1487865..ca720ee 100644 --- a/tests/tcltest.test +++ b/tests/tcltest.test @@ -1207,7 +1207,7 @@ test tcltest-21.2 {force a test command failure} { } {1} } -returnCodes 1 - -result {bad option "1": must be -body, -cleanup, -constraints, -errorOutput, -match, -output, -result, -returnCodes, or -setup} + -result {bad option "1": must be -body, -cleanup, -constraints, -errorCode, -errorOutput, -match, -output, -result, -returnCodes, or -setup} } test tcltest-21.3 {test command with setup} { @@ -1300,7 +1300,7 @@ test tcltest-21.7 {test command - bad flag} { } } -returnCodes 1 - -result {bad option "-foobar": must be -body, -cleanup, -constraints, -errorOutput, -match, -output, -result, -returnCodes, or -setup} + -result {bad option "-foobar": must be -body, -cleanup, -constraints, -errorCode, -errorOutput, -match, -output, -result, -returnCodes, or -setup} } # alternate test command format (these are the same as 21.1-21.6, with the @@ -1320,7 +1320,7 @@ test tcltest-21.8 {force a test command failure} \ } \ -returnCodes 1 \ -cleanup {set ::tcltest::currentFailure $fail} \ - -result {bad option "1": must be -body, -cleanup, -constraints, -errorOutput, -match, -output, -result, -returnCodes, or -setup} + -result {bad option "1": must be -body, -cleanup, -constraints, -errorCode, -errorOutput, -match, -output, -result, -returnCodes, or -setup} test tcltest-21.9 {test command with setup} \ -setup {set foo 1} \ -- cgit v0.12 From bf9af9a40854d7116b0d6a17fae0594ce3c38f09 Mon Sep 17 00:00:00 2001 From: pspjuth Date: Tue, 23 Oct 2018 21:06:30 +0000 Subject: Cleanup --- library/tcltest/tcltest.tcl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library/tcltest/tcltest.tcl b/library/tcltest/tcltest.tcl index 8b14142..c90d726 100644 --- a/library/tcltest/tcltest.tcl +++ b/library/tcltest/tcltest.tcl @@ -2022,11 +2022,10 @@ proc tcltest::test {name description args} { if {!$setupFailure && ($returnCode ni $returnCodes)} { set codeFailure 1 } - set errCodeFailure 0 + set errorCodeFailure 0 if {!$setupFailure && !$codeFailure && $returnCode == 1 && \ ![string match $errorCode $errorCodeRes(body)]} { - set codeFailure 1 - set errCodeFailure 1 + set errorCodeFailure 1 } # If expected output/error strings exist, we have to compare @@ -2122,7 +2121,7 @@ proc tcltest::test {name description args} { variable numTests if {!($setupFailure || $cleanupFailure || $coreFailure || $outputFailure || $errorFailure || $codeFailure - || $scriptFailure)} { + || $errorCodeFailure || $scriptFailure)} { if {$testLevel == 1} { incr numTests(Passed) if {[IsVerbose pass]} { @@ -2187,11 +2186,11 @@ proc tcltest::test {name description args} { ($match matching):\n$result" } } - if {$errCodeFailure} { - # TODO + if {$errorCodeFailure} { puts [outputChannel] "---- Error code was: '$errorCodeRes(body)'" puts [outputChannel] "---- Error code should have been: '$errorCode'" - } elseif {$codeFailure} { + } + if {$codeFailure} { switch -- $returnCode { 0 { set msg "Test completed normally" } 1 { set msg "Test generated error" } -- cgit v0.12 From f44fb9b4500e047fdb9e361a17f44fa46dffec2b Mon Sep 17 00:00:00 2001 From: dgp Date: Wed, 24 Oct 2018 14:33:54 +0000 Subject: Request the tcltest version that provides the testing facilities used. --- tests/assemble.test | 2 +- tests/dict.test | 2 +- tests/ioCmd.test | 2 +- tests/source.test | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/assemble.test b/tests/assemble.test index bea780a..05c1f9b 100644 --- a/tests/assemble.test +++ b/tests/assemble.test @@ -12,7 +12,7 @@ # Commands covered: assemble if {"::tcltest" ni [namespace children]} { - package require tcltest 2.2 + package require tcltest 2.5 namespace import -force ::tcltest::* } namespace eval tcl::unsupported {namespace export assemble} diff --git a/tests/dict.test b/tests/dict.test index 1479727..904ec53 100644 --- a/tests/dict.test +++ b/tests/dict.test @@ -10,7 +10,7 @@ # this file, and for a DISCLAIMER OF ALL WARRANTIES. if {[lsearch [namespace children] ::tcltest] == -1} { - package require tcltest 2 + package require tcltest 2.5 namespace import -force ::tcltest::* } diff --git a/tests/ioCmd.test b/tests/ioCmd.test index 3c4caa7..68bc542 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -14,7 +14,7 @@ # of this file, and for a DISCLAIMER OF ALL WARRANTIES. if {[lsearch [namespace children] ::tcltest] == -1} { - package require tcltest 2 + package require tcltest 2.5 namespace import -force ::tcltest::* } diff --git a/tests/source.test b/tests/source.test index ab6b447..8b146d3 100644 --- a/tests/source.test +++ b/tests/source.test @@ -12,8 +12,8 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. -if {[catch {package require tcltest 2.1}]} { - puts stderr "Skipping tests in [info script]. tcltest 2.1 required." +if {[catch {package require tcltest 2.5}]} { + puts stderr "Skipping tests in [info script]. tcltest 2.5 required." return } -- cgit v0.12