OpenFPGA/libs/EXTERNAL/tcl8.6.12/tests/ioTrans.test

2096 lines
58 KiB
Plaintext
Raw Normal View History

2022-06-07 11:15:20 -05:00
# -*- tcl -*-
# Functionality covered: operation of the reflected transformation
#
# This file contains a collection of tests for one or more of the Tcl
# built-in commands. Sourcing this file into Tcl runs the tests and
# generates output for errors. No output means no errors were found.
#
# Copyright (c) 2007 Andreas Kupries <andreask@activestate.com>
# <akupries@shaw.ca>
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
if {"::tcltest" ni [namespace children]} {
package require tcltest 2.5
namespace import -force ::tcltest::*
}
::tcltest::loadTestedCommands
catch [list package require -exact Tcltest [info patchlevel]]
# Custom constraints used in this file
testConstraint testchannel [llength [info commands testchannel]]
testConstraint thread [expr {0 == [catch {package require Thread 2.7-}]}]
# testchannel cut|splice Both needed to test the reflection in threads.
# thread::send
#----------------------------------------------------------------------
# ### ### ### ######### ######### #########
## Testing the reflected transformation.
# Helper commands to record the arguments to handler methods. Stored in a
# script so that the tests needing this code do not need their own copy but
# can access this variable.
set helperscript {
if {"::tcltest" ni [namespace children]} {
package require tcltest 2.5
namespace import -force ::tcltest::*
}
# This forces the return options to be in the order that the test expects!
variable optorder {
-code !?! -level !?! -errorcode !?! -errorline !?! -errorinfo !?!
-errorstack !?!
}
proc noteOpts opts {
variable optorder
lappend ::res [dict merge $optorder $opts]
}
# Helper command, canned result for 'initialize' method. Gets the
# optional methods as arguments. Use return features to post the result
# higher up.
proc handle.initialize {args} {
upvar args hargs
if {[lindex $hargs 0] eq "initialize"} {
return -code return [list {*}$args initialize finalize read write]
}
}
proc handle.finalize {} {
upvar args hargs
if {[lindex $hargs 0] eq "finalize"} {
return -code return ""
}
}
proc handle.read {} {
upvar args hargs
if {[lindex $hargs 0] eq "read"} {
return -code return "@"
}
}
proc handle.drain {} {
upvar args hargs
if {[lindex $hargs 0] eq "drain"} {
return -code return "<>"
}
}
proc handle.clear {} {
upvar args hargs
if {[lindex $hargs 0] eq "clear"} {
return -code return ""
}
}
proc tempchan {{mode r+}} {
global tempchan
return [set tempchan [open [makeFile {test data} tempchanfile] $mode]]
}
proc tempdone {} {
global tempchan
catch {close $tempchan}
removeFile tempchanfile
return
}
proc tempview {} { viewFile tempchanfile }
}
# Set everything up in the main thread.
eval $helperscript
#puts <<[file channels]>>
# ### ### ### ######### ######### #########
test iortrans-1.0 {chan, wrong#args} -returnCodes error -body {
chan
} -result {wrong # args: should be "chan subcommand ?arg ...?"}
test iortrans-1.1 {chan, unknown method} -returnCodes error -body {
chan foo
} -match glob -result {unknown or ambiguous subcommand "foo": must be*}
# --- --- --- --------- --------- ---------
# chan push, and method "initalize"
test iortrans-2.0 {chan push, wrong#args, not enough} -returnCodes error -body {
chan push
} -result {wrong # args: should be "chan push channel cmdprefix"}
test iortrans-2.1 {chan push, wrong#args, too many} -returnCodes error -body {
chan push a b c
} -result {wrong # args: should be "chan push channel cmdprefix"}
test iortrans-2.2 {chan push, invalid channel} -setup {
proc foo {} {}
} -returnCodes error -body {
chan push {} foo
} -cleanup {
rename foo {}
} -result {can not find channel named ""}
test iortrans-2.3 {chan push, bad handler, not a list} -body {
chan push [tempchan] "foo \{"
} -returnCodes error -cleanup {
tempdone
} -result {unmatched open brace in list}
test iortrans-2.4 {chan push, bad handler, not a command} -body {
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
} -result {invalid command name "foo"}
test iortrans-2.5 {chan push, initialize failed, bad signature} -body {
proc foo {} {}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -result {wrong # args: should be "foo"}
test iortrans-2.6 {chan push, initialize failed, bad signature} -body {
proc foo {} {}
chan push [tempchan] ::foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -result {wrong # args: should be "::foo"}
test iortrans-2.7 {chan push, initialize failed, bad result, not a list} -body {
proc foo {args} {return "\{"}
catch {chan push [tempchan] foo}
return $::errorInfo
} -cleanup {
tempdone
rename foo {}
} -match glob -result {chan handler "foo initialize" returned non-list: *}
test iortrans-2.8 {chan push, initialize failed, bad result, not a list} -body {
proc foo {args} {return \{\{\}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {chan handler "foo initialize" returned non-list: *}
test iortrans-2.9 {chan push, initialize failed, bad result, empty list} -body {
proc foo {args} {}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*all required methods*}
test iortrans-2.10 {chan push, initialize failed, bad result, bogus method name} -body {
proc foo {args} {return 1}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*bad method "1": must be *}
test iortrans-2.11 {chan push, initialize failed, bad result, bogus method name} -body {
proc foo {args} {return {a b c}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*bad method "c": must be *}
test iortrans-2.12 {chan push, initialize failed, bad result, required methods missing} -body {
# Required: initialize, and finalize.
proc foo {args} {return {initialize}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*all required methods*}
test iortrans-2.13 {chan push, initialize failed, bad result, illegal method name} -body {
proc foo {args} {return {initialize finalize BOGUS}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*returned bad method "BOGUS": must be clear, drain, finalize, flush, initialize, limit?, read, or write}
test iortrans-2.14 {chan push, initialize failed, bad result, mode/handler mismatch} -body {
proc foo {args} {return {initialize finalize}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*makes the channel inaccessible}
# iortrans-2.15 event/watch methods elimimated, removed these tests.
# iortrans-2.16
test iortrans-2.17 {chan push, initialize failed, bad result, drain/read mismatch} -body {
proc foo {args} {return {initialize finalize drain write}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*supports "drain" but not "read"}
test iortrans-2.18 {chan push, initialize failed, bad result, flush/write mismatch} -body {
proc foo {args} {return {initialize finalize flush read}}
chan push [tempchan] foo
} -returnCodes error -cleanup {
tempdone
rename foo {}
} -match glob -result {*supports "flush" but not "write"}
test iortrans-2.19 {chan push, initialize ok, creates channel} -setup {
set res {}
} -match glob -body {
proc foo {args} {
global res
lappend res $args
if {[lindex $args 0] ne "initialize"} {return}
return {initialize finalize drain flush read write}
}
lappend res [file channel rt*]
lappend res [chan push [tempchan] foo]
lappend res [close [lindex $res end]]
lappend res [file channel rt*]
} -cleanup {
tempdone
rename foo {}
} -result {{} {initialize rt* {read write}} file* {drain rt*} {flush rt*} {finalize rt*} {} {}}
test iortrans-2.20 {chan push, init failure -> no channel, no finalize} -setup {
set res {}
} -match glob -body {
proc foo {args} {
global res
lappend res $args
return
}
lappend res [file channel rt*]
lappend res [catch {chan push [tempchan] foo} msg] $msg
lappend res [file channel rt*]
} -cleanup {
tempdone
rename foo {}
} -result {{} {initialize rt* {read write}} 1 {*all required methods*} {}}
# --- --- --- --------- --------- ---------
# method finalize (via close)
# General note: file channels rt* finds the transform channel, however the
# name reported will be that of the underlying base driver, fileXX here. This
# actually allows us to see if the whole channel is gone, or only the
# transformation, but not the base.
test iortrans-3.1 {chan finalize, handler destruction has no effect on channel} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return
}
lappend res [set c [chan push [tempchan] foo]]
rename foo {}
lappend res [file channels file*]
lappend res [file channels rt*]
lappend res [catch {close $c} msg] $msg
lappend res [file channels file*]
lappend res [file channels rt*]
} -cleanup {
tempdone
} -result {{initialize rt* {read write}} file* file* {} 1 {invalid command name "foo"} {} {}}
test iortrans-3.2 {chan finalize, for close} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return
}
lappend res [set c [chan push [tempchan] foo]]
close $c
# Close deleted the channel.
lappend res [file channels rt*]
# Channel destruction does not kill handler command!
lappend res [info command foo]
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} {} foo}
test iortrans-3.3 {chan finalize, for close, error, close error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code error 5
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg] $msg
# Channel is gone despite error.
lappend res [file channels rt*]
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 5 {}}
test iortrans-3.4 {chan finalize, for close, error, close error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
error FOO
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg] $msg $::errorInfo
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 FOO {FOO
*"close $c"}}
test iortrans-3.5 {chan finalize, for close, arbitrary result, ignored} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return SOMETHING
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg] $msg
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 0 {}}
test iortrans-3.6 {chan finalize, for close, break, close error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code 3
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg] $msg
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code*}
test iortrans-3.7 {chan finalize, for close, continue, close error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code 4
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg] $msg
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code*}
test iortrans-3.8 {chan finalize, for close, custom code, close error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code 777 BANG
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg] $msg
} -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code*}
test iortrans-3.9 {chan finalize, for close, ignore level, close error} -setup {
set res {}
} -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -level 5 -code 777 BANG
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [catch {close $c} msg opt] $msg
noteOpts $opt
} -match glob -cleanup {
rename foo {}
tempdone
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "finalize"*}}
# --- === *** ###########################
# method read (via read)
test iortrans-4.1 {chan read, transform call and return} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return snarf
}
set c [chan push [tempchan] foo]
lappend res [read $c 10]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} snarf}
test iortrans-4.2 {chan read, for non-readable channel} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args MUST_NOT_HAPPEN
}
set c [chan push [tempchan w] foo]
lappend res [catch {read $c 2} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {1 {channel "file*" wasn't opened for reading}}
test iortrans-4.3 {chan read, error return} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code error BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {read $c 2} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} 1 BOOM!}
test iortrans-4.4 {chan read, break return is error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code break BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {read $c 2} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} 1 *bad code*}
test iortrans-4.5 {chan read, continue return is error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code continue BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {read $c 2} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} 1 *bad code*}
test iortrans-4.6 {chan read, custom return is error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {read $c 2} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} 1 *bad code*}
test iortrans-4.7 {chan read, level is squashed} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -level 55 -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {read $c 2} msg opt] $msg
noteOpts $opt
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "read"*}}
test iortrans-4.8 {chan read, read, bug 2921116} -setup {
set res {}
} -match glob -body {
proc foo {fd args} {
handle.initialize
handle.finalize
lappend ::res $args
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
}
set c [chan push [set c [tempchan]] [list foo $c]]
lappend res [read $c]
#lappend res [gets $c]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} {}}
test iortrans-4.8.1 {chan read, bug 721ec69271} -setup {
set res {}
} -match glob -body {
proc foo {fd args} {
handle.initialize
handle.finalize
lappend ::res $args
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
}
set c [chan push [set c [tempchan]] [list foo $c]]
chan configure $c -buffersize 2
lappend res [read $c]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* te} {read rt* st} {read rt* { d}} {read rt* at} {read rt* {a
}} {}}
test iortrans-4.8.2 {chan read, bug 721ec69271} -setup {
set res {}
} -match glob -body {
proc foo {fd args} {
handle.initialize
handle.finalize
lappend ::res $args
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
return x
}
set c [chan push [set c [tempchan]] [list foo $c]]
chan configure $c -buffersize 1
lappend res [read $c]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* t} {read rt* e} {read rt* s} {read rt* t} {read rt* { }} {read rt* d} {read rt* a} {read rt* t} {read rt* a} {read rt* {
}} {}}
test iortrans-4.9 {chan read, gets, bug 2921116} -setup {
set res {}
} -match glob -body {
proc foo {fd args} {
handle.initialize
handle.finalize
lappend ::res $args
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
}
set c [chan push [set c [tempchan]] [list foo $c]]
lappend res [gets $c]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} {}}
# Driver for a base channel that emits several short "files"
# with each terminated by a fleeting EOF
proc driver {cmd args} {
variable ::tcl::buffer
variable ::tcl::index
set chan [lindex $args 0]
switch -- $cmd {
initialize {
set index($chan) 0
set buffer($chan) .....
return {initialize finalize watch read}
}
finalize {
if {![info exists index($chan)]} {return}
unset index($chan) buffer($chan)
array unset index
array unset buffer
return
}
watch {}
read {
set n [lindex $args 1]
if {![info exists index($chan)]} {
driver initialize $chan
}
set new [expr {$index($chan) + $n}]
set result [string range $buffer($chan) $index($chan) $new-1]
set index($chan) $new
if {[string length $result] == 0} {
driver finalize $chan
}
return $result
}
}
}
# Channel read transform that is just the identity - pass all through
proc idxform {cmd handle args} {
switch -- $cmd {
initialize {
return {initialize finalize read}
}
finalize {
return
}
read {
lassign $args buffer
return $buffer
}
}
}
# Test that all EOFs pass through full xform stack. Proper data boundaries.
# Check robustness against buffer sizes.
test iortrans-4.10 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] idxform]
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
test iortrans-4.10.1 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] idxform]
chan configure $chan -buffersize 3
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
test iortrans-4.10.2 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] idxform]
chan configure $chan -buffersize 5
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
rename idxform {}
# Channel read transform that delays the data and always returns something
proc delayxform {cmd handle args} {
variable store
switch -- $cmd {
initialize {
set store($handle) {}
return {initialize finalize read drain}
}
finalize {
unset store($handle)
return
}
read {
lassign $args buffer
if {$store($handle) eq {}} {
set reply [string index $buffer 0]
set store($handle) [string range $buffer 1 end]
} else {
set reply $store($handle)
set store($handle) $buffer
}
return $reply
}
drain {
delayxform read $handle {}
}
}
}
# Test that all EOFs pass through full xform stack. Proper data boundaries.
# Check robustness against buffer sizes.
test iortrans-4.11 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] delayxform]
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
test iortrans-4.11.1 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] delayxform]
chan configure $chan -buffersize 3
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
test iortrans-4.11.2 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] delayxform]
chan configure $chan -buffersize 5
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
rename delayxform {}
# Channel read transform that delays the data and may return {}
proc delay2xform {cmd handle args} {
variable store
switch -- $cmd {
initialize {
set store($handle) {}
return {initialize finalize read drain}
}
finalize {
unset store($handle)
return
}
read {
lassign $args buffer
set reply $store($handle)
set store($handle) $buffer
return $reply
}
drain {
delay2xform read $handle {}
}
}
}
test iortrans-4.12 {[5adbc350683] chan read, handle fleeting EOF} -body {
set chan [chan push [chan create read driver] delay2xform]
list [eof $chan] [read $chan] [eof $chan] [read $chan 0] [eof $chan] \
[read $chan] [eof $chan]
} -cleanup {
close $chan
} -result {0 ..... 1 {} 0 ..... 1}
rename delay2xform {}
rename driver {}
# --- === *** ###########################
# method write (via puts)
test iortrans-5.1 {chan write, regular write} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return transformresult
}
set c [chan push [tempchan] foo]
puts -nonewline $c snarf
flush $c
close $c
lappend res [tempview]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarf} transformresult}
test iortrans-5.2 {chan write, no write is ok, no change to file} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return
}
set c [chan push [tempchan] foo]
puts -nonewline $c snarfsnarfsnarf
flush $c
close $c
lappend res [tempview]; # This has to show the original data, as nothing was written
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} {test data}}
test iortrans-5.3 {chan write, failed write} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code error FAIL!
}
set c [chan push [tempchan] foo]
puts -nonewline $c snarfsnarfsnarf
lappend res [catch {flush $c} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 FAIL!}
test iortrans-5.4 {chan write, non-writable channel} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args MUST_NOT_HAPPEN
return
}
set c [chan push [tempchan r] foo]
lappend res [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
} -cleanup {
close $c
tempdone
rename foo {}
} -result {1 {channel "file*" wasn't opened for writing}}
test iortrans-5.5 {chan write, failed write, error return} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code error BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 BOOM!}
test iortrans-5.6 {chan write, failed write, error return} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
error BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 BOOM!}
test iortrans-5.7 {chan write, failed write, break return is error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code break BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code*}
test iortrans-5.8 {chan write, failed write, continue return is error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code continue BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code*}
test iortrans-5.9 {chan write, failed write, custom return is error} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code*}
test iortrans-5.10 {chan write, failed write, level is ignored} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -level 55 -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg opt] $msg
noteOpts $opt
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline * -errorinfo *bad code*subcommand "write"*}}
test iortrans-5.11 {chan write, bug 2921116} -match glob -setup {
set res {}
set level 0
} -body {
proc foo {fd args} {
handle.initialize
handle.finalize
lappend ::res $args
# pop - invokes flush - invokes 'foo write' - infinite recursion - stop it
global level
if {$level} {
return
}
incr level
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
}
set c [chan push [set c [tempchan]] [list foo $c]]
lappend res [puts -nonewline $c abcdef]
lappend res [flush $c]
} -cleanup {
tempdone
rename foo {}
} -result {{} {write rt* abcdef} {write rt* abcdef} {}}
# --- === *** ###########################
# method limit?, drain (via read)
test iortrans-6.1 {chan read, read limits} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize limit?
handle.finalize
lappend ::res $args
handle.read
return 6
}
set c [chan push [tempchan] foo]
lappend res [read $c 10]
} -cleanup {
tempdone
rename foo {}
} -result {{limit? rt*} {read rt* {test d}} {limit? rt*} {read rt* {ata
}} {limit? rt*} @@}
test iortrans-6.2 {chan read, read transform drain on eof} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize drain
handle.finalize
lappend ::res $args
handle.read
handle.drain
return
}
set c [chan push [tempchan] foo]
lappend res [read $c]
lappend res [close $c]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} {drain rt*} @<> {}}
# --- === *** ###########################
# method clear (via puts, seek)
test iortrans-7.1 {chan write, write clears read buffers} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize clear
handle.finalize
lappend ::res $args
handle.clear
return transformresult
}
set c [chan push [tempchan] foo]
puts -nonewline $c snarf
flush $c
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*} {write rt* snarf}}
test iortrans-7.2 {seek clears read buffers} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize clear
handle.finalize
lappend ::res $args
return
}
set c [chan push [tempchan] foo]
seek $c 2
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*}}
test iortrans-7.3 {clear, any result is ignored} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize clear
handle.finalize
lappend ::res $args
return -code error "X"
}
set c [chan push [tempchan] foo]
seek $c 2
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*}}
test iortrans-7.4 {chan clear, bug 2921116} -match glob -setup {
set res {}
} -body {
proc foo {fd args} {
handle.initialize clear
handle.finalize
lappend ::res $args
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
}
set c [chan push [set c [tempchan]] [list foo $c]]
seek $c 2
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*}}
# --- === *** ###########################
# method flush (via seek, close)
test iortrans-8.1 {seek flushes write buffers, ignores data} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize flush
handle.finalize
lappend ::res $args
return X
}
set c [chan push [tempchan] foo]
# Flush, no writing
seek $c 2
# The close flushes again, this modifies the file!
lappend res |
lappend res [close $c] | [tempview]
} -cleanup {
tempdone
rename foo {}
} -result {{flush rt*} | {flush rt*} {} | {teXt data}}
test iortrans-8.2 {close flushes write buffers, writes data} -setup {
set res {}
} -match glob -body {
proc foo {args} {
handle.initialize flush
lappend ::res $args
handle.finalize
return .flushed.
}
set c [chan push [tempchan] foo]
close $c
lappend res [tempview]
} -cleanup {
tempdone
rename foo {}
} -result {{flush rt*} {finalize rt*} .flushed.}
test iortrans-8.3 {chan flush, bug 2921116} -match glob -setup {
set res {}
} -body {
proc foo {fd args} {
handle.initialize flush
handle.finalize
lappend ::res $args
# Kill and recreate transform while it is operating
chan pop $fd
chan push $fd [list foo $fd]
}
set c [chan push [set c [tempchan]] [list foo $c]]
seek $c 2
set res
} -cleanup {
tempdone
rename foo {}
} -result {{flush rt*}}
# --- === *** ###########################
# method watch - removed from TIP (rev 1.12+)
# --- === *** ###########################
# method event - removed from TIP (rev 1.12+)
# --- === *** ###########################
# 'Pull the rug' tests. Create channel in a interpreter A, move to other
# interpreter B, destroy the origin interpreter (A) before or during access
# from B. Must not crash, must return proper errors.
test iortrans-11.0 {origin interpreter of moved transform gone} -setup {
set ida [interp create]; #puts <<$ida>>
set idb [interp create]; #puts <<$idb>>
# Magic to get the test* commands in the children
load {} Tcltest $ida
load {} Tcltest $idb
} -constraints {testchannel} -match glob -body {
# Set up channel and transform in interpreter
interp eval $ida $helperscript
interp eval $ida [list ::variable tempchan [tempchan]]
interp transfer {} $::tempchan $ida
set chan [interp eval $ida {
variable tempchan
proc foo {args} {
handle.initialize clear drain flush limit? read write
handle.finalize
lappend ::res $args
return
}
set chan [chan push $tempchan foo]
fconfigure $chan -buffering none
set chan
}]
# Move channel to 2nd interpreter, transform goes with it.
interp eval $ida [list testchannel cut $chan]
interp eval $idb [list testchannel splice $chan]
# Kill origin interpreter, then access channel from 2nd interpreter.
interp delete $ida
set res {}
lappend res \
[catch {interp eval $idb [list puts $chan shoo]} msg] $msg \
[catch {interp eval $idb [list tell $chan]} msg] $msg \
[catch {interp eval $idb [list seek $chan 1]} msg] $msg \
[catch {interp eval $idb [list gets $chan]} msg] $msg \
[catch {interp eval $idb [list close $chan]} msg] $msg
#lappend res [interp eval $ida {set res}]
# actions: clear|write|clear|write|clear|flush|limit?|drain|flush
# The 'tell' is ok, as it passed through the transform to the base channel
# without invoking the transform handler.
} -cleanup {
tempdone
interp delete $idb
} -result {1 {Owner lost} 0 0 1 {Owner lost} 1 {Owner lost} 1 {Owner lost}}
test iortrans-11.1 {origin interpreter of moved transform destroyed during access} -setup {
set ida [interp create]; #puts <<$ida>>
set idb [interp create]; #puts <<$idb>>
# Magic to get the test* commands in the children
load {} Tcltest $ida
load {} Tcltest $idb
} -constraints {testchannel} -match glob -body {
# Set up channel in thread
set chan [interp eval $ida $helperscript]
interp eval $ida [list ::variable tempchan [tempchan]]
interp transfer {} $::tempchan $ida
set chan [interp eval $ida {
proc foo {args} {
handle.initialize clear drain flush limit? read write
handle.finalize
lappend ::res $args
# Destroy interpreter during channel access.
suicide
}
set chan [chan push $tempchan foo]
fconfigure $chan -buffering none
set chan
}]
interp alias $ida suicide {} interp delete $ida
# Move channel to 2nd thread, transform goes with it.
interp eval $ida [list testchannel cut $chan]
interp eval $idb [list testchannel splice $chan]
# Run access from interpreter B, this will give us a synchronous response.
interp eval $idb [list set chan $chan]
interp eval $idb [list set mid $tcltest::mainThread]
set res [interp eval $idb {
# Wait a bit, give the main thread the time to start its event loop to
# wait for the response from B
after 50
catch { puts $chan shoo } res
set res
}]
} -cleanup {
interp delete $idb
tempdone
} -result {Owner lost}
test iortrans-11.2 {delete interp of reflected transform} -setup {
interp create child
# Magic to get the test* commands into the child
load {} Tcltest child
} -constraints {testchannel} -body {
# Get base channel into the child
set c [tempchan]
testchannel cut $c
interp eval child [list testchannel splice $c]
interp eval child [list set c $c]
child eval {
proc no-op args {}
proc driver {c sub args} {
return {initialize finalize read write}
}
set t [chan push $c [list driver $c]]
chan event $c readable no-op
}
interp delete child
} -cleanup {
tempdone
} -result {}
# ### ### ### ######### ######### #########
## Same tests as above, but exercising the code forwarding and receiving
## driver operations to the originator thread.
# ### ### ### ######### ######### #########
## Testing the reflected channel (Thread forwarding).
#
## The id numbers refer to the original test without thread forwarding, and
## gaps due to tests not applicable to forwarding are left to keep this
## association.
# ### ### ### ######### ######### #########
## Helper command. Runs a script in a separate thread and returns the result.
## A channel is transfered into the thread as well, and a list of configuation
## variables
proc inthread {chan script args} {
# Test thread.
set tid [thread::create -preserved]
thread::send $tid {load {} Tcltest}
# Init thread configuration.
# - Listed variables
# - Id of main thread
# - A number of helper commands
foreach v $args {
upvar 1 $v x
thread::send $tid [list set $v $x]
}
thread::send $tid [list set mid [thread::id]]
thread::send $tid {
proc notes {} {
return $::notes
}
proc noteOpts opts {
lappend ::notes [dict merge {
-code !?! -level !?! -errorcode !?! -errorline !?!
-errorinfo !?! -errorstack !?!
} $opts]
}
}
thread::send $tid [list proc s {} [list uplevel 1 $script]]; # (*)
# Transfer channel (cut/splice aka detach/attach)
testchannel cut $chan
thread::send $tid [list testchannel splice $chan]
# Run test script, also run local event loop! The local event loop waits
# for the result to come back. It is also necessary for the execution of
# forwarded channel operations.
set ::tres ""
thread::send -async $tid {
after 50
catch {s} res; # This runs the script, 's' was defined at (*)
thread::send -async $mid [list set ::tres $res]
}
vwait ::tres
# Remove test thread, and return the captured result.
thread::release $tid
return $::tres
}
# ### ### ### ######### ######### #########
test iortrans.tf-3.2 {chan finalize, for close} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return {}
}
lappend res [set c [chan push [tempchan] foo]]
lappend res [inthread $c {
close $c
# Close the deleted the channel.
file channels rt*
} c]
# Channel destruction does not kill handler command!
lappend res [info command foo]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} {} foo}
test iortrans.tf-3.3 {chan finalize, for close, error, close error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code error 5
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg] $msg
# Channel is gone despite error.
lappend notes [file channels rt*]
notes
} c]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 5 {}}
test iortrans.tf-3.4 {chan finalize, for close, error, close errror} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
lappend ::res $args
handle.initialize
error FOO
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg] $msg
notes
} c]
} -match glob -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 FOO}
test iortrans.tf-3.5 {chan finalize, for close, arbitrary result} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return SOMETHING
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg] $msg
notes
} c]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 0 {}}
test iortrans.tf-3.6 {chan finalize, for close, break, close error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code 3
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg] $msg
notes
} c]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code*}
test iortrans.tf-3.7 {chan finalize, for close, continue, close error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code 4
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg] $msg
notes
} c]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code*}
test iortrans.tf-3.8 {chan finalize, for close, custom code, close error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -code 777 BANG
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg] $msg
notes
} c]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code*}
test iortrans.tf-3.9 {chan finalize, for close, ignore level, close error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
lappend ::res $args
handle.initialize
return -level 5 -code 777 BANG
}
lappend res [set c [chan push [tempchan] foo]]
lappend res {*}[inthread $c {
lappend notes [catch {close $c} msg opt] $msg
noteOpts $opt
notes
} c]
} -cleanup {
rename foo {}
} -result {{initialize rt* {read write}} file* {finalize rt*} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "finalize"*}}
# --- === *** ###########################
# method read
test iortrans.tf-4.1 {chan read, transform call and return} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return snarf
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [read $c 10]
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{read rt* {test data
}} snarf}
test iortrans.tf-4.2 {chan read, for non-readable channel} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args MUST_NOT_HAPPEN
}
set c [chan push [tempchan w] foo]
lappend res {*}[inthread $c {
lappend notes [catch {[read $c 2]} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {1 {channel "file*" wasn't opened for reading}}
test iortrans.tf-4.3 {chan read, error return} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code error BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {read $c 2} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{read rt* {test data
}} 1 BOOM!}
test iortrans.tf-4.4 {chan read, break return is error} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code break BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {read $c 2} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{read rt* {test data
}} 1 *bad code*}
test iortrans.tf-4.5 {chan read, continue return is error} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code continue BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {read $c 2} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{read rt* {test data
}} 1 *bad code*}
test iortrans.tf-4.6 {chan read, custom return is error} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {read $c 2} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{read rt* {test data
}} 1 *bad code*}
test iortrans.tf-4.7 {chan read, level is squashed} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -level 55 -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {read $c 2} msg opt] $msg
noteOpts $opt
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{read rt* {test data
}} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "read"*}}
# --- === *** ###########################
# method write
test iortrans.tf-5.1 {chan write, regular write} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return transformresult
}
set c [chan push [tempchan] foo]
inthread $c {
puts -nonewline $c snarf
flush $c
close $c
} c
lappend res [tempview]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarf} transformresult}
test iortrans.tf-5.2 {chan write, no write is ok, no change to file} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return
}
set c [chan push [tempchan] foo]
inthread $c {
puts -nonewline $c snarfsnarfsnarf
flush $c
close $c
} c
lappend res [tempview]; # This has to show the original data, as nothing was written
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} {test data}}
test iortrans.tf-5.3 {chan write, failed write} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code error FAIL!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
puts -nonewline $c snarfsnarfsnarf
lappend notes [catch {flush $c} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 FAIL!}
test iortrans.tf-5.4 {chan write, non-writable channel} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args MUST_NOT_HAPPEN
return
}
set c [chan push [tempchan r] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {1 {channel "file*" wasn't opened for writing}}
test iortrans.tf-5.5 {chan write, failed write, error return} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code error BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 BOOM!}
test iortrans.tf-5.6 {chan write, failed write, error return} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
error BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 BOOM!}
test iortrans.tf-5.7 {chan write, failed write, break return is error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code break BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code*}
test iortrans.tf-5.8 {chan write, failed write, continue return is error} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code continue BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
close $c
notes
} c]
} -cleanup {
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code*}
test iortrans.tf-5.9 {chan write, failed write, custom return is error} -setup {
set res {}
} -constraints {testchannel thread} -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg] $msg
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -match glob -result {{write rt* snarfsnarfsnarf} 1 *bad code*}
test iortrans.tf-5.10 {chan write, failed write, level is ignored} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize
handle.finalize
lappend ::res $args
return -level 55 -code 777 BOOM!
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [catch {
puts -nonewline $c snarfsnarfsnarf
flush $c
} msg opt] $msg
noteOpts $opt
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{write rt* snarfsnarfsnarf} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline * -errorinfo *bad code*subcommand "write"*}}
# --- === *** ###########################
# method limit?, drain (via read)
test iortrans.tf-6.1 {chan read, read limits} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize limit?
handle.finalize
lappend ::res $args
handle.read
return 6
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [read $c 10]
close $c
notes
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{limit? rt*} {read rt* {test d}} {limit? rt*} {read rt* {ata
}} {limit? rt*} @@}
test iortrans.tf-6.2 {chan read, read transform drain on eof} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize drain
handle.finalize
lappend ::res $args
handle.read
handle.drain
return
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
lappend notes [read $c]
lappend notes [close $c]
} c]
} -cleanup {
tempdone
rename foo {}
} -result {{read rt* {test data
}} {drain rt*} @<> {}}
# --- === *** ###########################
# method clear (via puts, seek)
test iortrans.tf-7.1 {chan write, write clears read buffers} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize clear
handle.finalize
lappend ::res $args
handle.clear
return transformresult
}
set c [chan push [tempchan] foo]
inthread $c {
puts -nonewline $c snarf
flush $c
close $c
} c
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*} {write rt* snarf}}
test iortrans.tf-7.2 {seek clears read buffers} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize clear
handle.finalize
lappend ::res $args
return
}
set c [chan push [tempchan] foo]
inthread $c {
seek $c 2
close $c
} c
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*}}
test iortrans.tf-7.3 {clear, any result is ignored} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize clear
handle.finalize
lappend ::res $args
return -code error "X"
}
set c [chan push [tempchan] foo]
inthread $c {
seek $c 2
close $c
} c
return $res
} -cleanup {
tempdone
rename foo {}
} -result {{clear rt*}}
# --- === *** ###########################
# method flush (via seek, close)
test iortrans.tf-8.1 {seek flushes write buffers, ignores data} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize flush
handle.finalize
lappend ::res $args
return X
}
set c [chan push [tempchan] foo]
lappend res {*}[inthread $c {
# Flush, no writing
seek $c 2
# The close flushes again, this modifies the file!
lappend notes | [close $c] |
# NOTE: The flush generated by the close is recorded immediately, the
# other note's here are defered until after the thread is done. This
# changes the order of the result a bit from the non-threaded case
# (The first | moves one to the right). This is an artifact of the
# 'inthread' framework, not of the transformation itself.
notes
} c]
lappend res [tempview]
} -cleanup {
tempdone
rename foo {}
} -result {{flush rt*} {flush rt*} | {} | {teXt data}}
test iortrans.tf-8.2 {close flushes write buffers, writes data} -setup {
set res {}
} -constraints {testchannel thread} -match glob -body {
proc foo {args} {
handle.initialize flush
lappend ::res $args
handle.finalize
return .flushed.
}
set c [chan push [tempchan] foo]
inthread $c {
close $c
} c
lappend res [tempview]
} -cleanup {
tempdone
rename foo {}
} -result {{flush rt*} {finalize rt*} .flushed.}
# --- === *** ###########################
# method watch - removed from TIP (rev 1.12+)
# --- === *** ###########################
# method event - removed from TIP (rev 1.12+)
# --- === *** ###########################
# 'Pull the rug' tests. Create channel in a thread A, move to other thread B,
# destroy the origin thread (A) before or during access from B. Must not
# crash, must return proper errors.
test iortrans.tf-11.0 {origin thread of moved transform gone} -setup {
#puts <<$tcltest::mainThread>>main
set tida [thread::create -preserved]; #puts <<$tida>>
thread::send $tida {load {} Tcltest}
set tidb [thread::create -preserved]; #puts <<$tida>>
thread::send $tidb {load {} Tcltest}
} -constraints {testchannel thread} -match glob -body {
# Set up channel in thread
thread::send $tida $helperscript
thread::send $tidb $helperscript
set chan [thread::send $tida {
proc foo {args} {
handle.initialize clear drain flush limit? read write
handle.finalize
lappend ::res $args
return
}
set chan [chan push [tempchan] foo]
fconfigure $chan -buffering none
set chan
}]
# Move channel to 2nd thread, transform goes with it.
thread::send $tida [list testchannel cut $chan]
thread::send $tidb [list testchannel splice $chan]
# Kill origin thread, then access channel from 2nd thread.
thread::release -wait $tida
set res {}
lappend res [catch {thread::send $tidb [list puts $chan shoo]} msg] $msg
lappend res [catch {thread::send $tidb [list tell $chan]} msg] $msg
lappend res [catch {thread::send $tidb [list seek $chan 1]} msg] $msg
lappend res [catch {thread::send $tidb [list gets $chan]} msg] $msg
lappend res [catch {thread::send $tidb [list close $chan]} msg] $msg
# The 'tell' is ok, as it passed through the transform to the base
# channel without invoking the transform handler.
} -cleanup {
thread::send $tidb tempdone
thread::release $tidb
} -result {1 {Owner lost} 0 0 1 {Owner lost} 1 {Owner lost} 1 {Owner lost}}
testConstraint notValgrind [expr {![testConstraint valgrind]}]
test iortrans.tf-11.1 {origin thread of moved transform destroyed during access} -setup {
#puts <<$tcltest::mainThread>>main
set tida [thread::create -preserved]; #puts <<$tida>>
thread::send $tida {load {} Tcltest}
set tidb [thread::create -preserved]; #puts <<$tidb>>
thread::send $tidb {load {} Tcltest}
} -constraints {testchannel thread notValgrind} -match glob -body {
# Set up channel in thread
thread::send $tida $helperscript
thread::send $tidb $helperscript
set chan [thread::send $tida {
proc foo {args} {
handle.initialize clear drain flush limit? read write
handle.finalize
lappend ::res $args
# destroy thread during channel access
thread::exit
}
set chan [chan push [tempchan] foo]
fconfigure $chan -buffering none
set chan
}]
# Move channel to 2nd thread, transform goes with it.
thread::send $tida [list testchannel cut $chan]
thread::send $tidb [list testchannel splice $chan]
# Run access from thread B, wait for response from A (A is not using event
# loop at this point, so the event pile up in the queue.
thread::send $tidb [list set chan $chan]
thread::send $tidb [list set mid [thread::id]]
thread::send -async $tidb {
# Wait a bit, give the main thread the time to start its event loop to
# wait for the response from B
after 50
catch { puts $chan shoo } res
catch { close $chan }
thread::send -async $mid [list set ::res $res]
}
vwait ::res
set res
} -cleanup {
thread::send $tidb tempdone
thread::release $tidb
} -result {Owner lost}
# ### ### ### ######### ######### #########
cleanupTests
return