summaryrefslogtreecommitdiffstats
path: root/tests/ioTrans.test
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ioTrans.test')
-rw-r--r--tests/ioTrans.test2093
1 files changed, 2093 insertions, 0 deletions
diff --git a/tests/ioTrans.test b/tests/ioTrans.test
new file mode 100644
index 0000000..63a609f
--- /dev/null
+++ b/tests/ioTrans.test
@@ -0,0 +1,2093 @@
+# -*- 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 {[lsearch [namespace children] ::tcltest] == -1} {
+ package require tcltest 2
+ 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 {[lsearch [namespace children] ::tcltest] == -1} {
+ package require tcltest 2
+ 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 slaves
+ 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
+} -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 slaves
+ 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 {
+ tempdone
+} -result {Owner lost}
+test iortrans-11.2 {delete interp of reflected transform} -setup {
+ interp create slave
+ # Magic to get the test* commands into the slave
+ load {} Tcltest slave
+} -constraints {testchannel} -body {
+ # Get base channel into the slave
+ set c [tempchan]
+ testchannel cut $c
+ interp eval slave [list testchannel splice $c]
+ interp eval slave [list set c $c]
+ slave 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 slave
+} -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