# Commands tested in this file: socket. # # 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) 1994-1996 Sun Microsystems, Inc. # Copyright (c) 1998-2000 Ajuba Solutions. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: socket.test,v 1.44 2010/09/28 15:13:55 rmax Exp $ # Running socket tests with a remote server: # ------------------------------------------ # # Some tests in socket.test depend on the existence of a remote server to # which they connect. The remote server must be an instance of tcltest and it # must run the script found in the file "remote.tcl" in this directory. You # can start the remote server on any machine reachable from the machine on # which you want to run the socket tests, by issuing: # # tcltest remote.tcl -port 2048 # Or choose another port number. # # If the machine you are running the remote server on has several IP # interfaces, you can choose which interface the server listens on for # connections by specifying the -address command line flag, so: # # tcltest remote.tcl -address your.machine.com # # These options can also be set by environment variables. On Unix, you can # type these commands to the shell from which the remote server is started: # # shell% setenv serverPort 2048 # shell% setenv serverAddress your.machine.com # # and subsequently you can start the remote server with: # # tcltest remote.tcl # # to have it listen on port 2048 on the interface your.machine.com. # # When the server starts, it prints out a detailed message containing its # configuration information, and it will block until killed with a Ctrl-C. # Once the remote server exists, you can run the tests in socket.test with the # server by setting two Tcl variables: # # % set remoteServerIP # % set remoteServerPort 2048 # # These variables are also settable from the environment. On Unix, you can: # # shell% setenv remoteServerIP machine.where.server.runs # shell% senetv remoteServerPort 2048 # # The preamble of the socket.test file checks to see if the variables are set # either in Tcl or in the environment; if they are, it attempts to connect to # the server. If the connection is successful, the tests using the remote # server will be performed; otherwise, it will attempt to start the remote # server (via exec) on platforms that support this, on the local host, # listening at port 2048. If all fails, a message is printed and the tests # using the remote server are not performed. package require tcltest 2 namespace import -force ::tcltest::* # Some tests require the testthread and exec commands testConstraint testthread [llength [info commands testthread]] testConstraint exec [llength [info commands exec]] # Produce a random port number in the Dynamic/Private range # from 49152 through 65535. proc randport {} { expr {int(rand()*16383+49152)} } # If remoteServerIP or remoteServerPort are not set, check in the environment # variables for externally set values. # if {![info exists remoteServerIP]} { if {[info exists env(remoteServerIP)]} { set remoteServerIP $env(remoteServerIP) } } if {![info exists remoteServerPort]} { if {[info exists env(remoteServerPort)]} { set remoteServerPort $env(remoteServerPort) } else { if {[info exists remoteServerIP]} { set remoteServerPort 2048 } } } # # Check if we're supposed to do tests against the remote server # set doTestsWithRemoteServer 1 if {![info exists remoteServerIP]} { set remoteServerIP 127.0.0.1 } if {($doTestsWithRemoteServer == 1) && (![info exists remoteServerPort])} { set remoteServerPort [randport] } # Attempt to connect to a remote server if one is already running. If it is # not running or for some other reason the connect fails, attempt to start the # remote server on the local host listening on port 2048. This is only done on # platforms that support exec (i.e. not on the Mac). On platforms that do not # support exec, the remote server must be started by the user before running # the tests. set remoteProcChan "" set commandSocket "" if {$doTestsWithRemoteServer} { catch {close $commandSocket} if {![catch { set commandSocket [socket $remoteServerIP $remoteServerPort] }]} then { fconfigure $commandSocket -translation crlf -buffering line } elseif {![testConstraint exec]} { set noRemoteTestReason "can't exec" set doTestsWithRemoteServer 0 } else { set remoteServerIP 127.0.0.1 # Be *extra* careful in case this file is sourced from # a directory other than the current one... set remoteFile [file join [pwd] [file dirname [info script]] \ remote.tcl] if {![catch { set remoteProcChan [open "|[list \ [interpreter] $remoteFile -serverIsSilent \ -port $remoteServerPort -address $remoteServerIP]" w+] } msg]} then { after 1000 if {[catch { set commandSocket [socket $remoteServerIP $remoteServerPort] } msg] == 0} then { fconfigure $commandSocket -translation crlf -buffering line } else { set noRemoteTestReason $msg set doTestsWithRemoteServer 0 } } else { set noRemoteTestReason "$msg [interpreter]" set doTestsWithRemoteServer 0 } } } # Some tests are run only if we are doing testing against a remote server. testConstraint doTestsWithRemoteServer $doTestsWithRemoteServer if {!$doTestsWithRemoteServer} { if {[string first s $::tcltest::verbose] != -1} { puts "Skipping tests with remote server. See tests/socket.test for" puts "information on how to run remote server." puts "Reason for not doing remote tests: $noRemoteTestReason" } } # # If we do the tests, define a command to send a command to the remote server. # if {[testConstraint doTestsWithRemoteServer]} { proc sendCommand {c} { global commandSocket if {[eof $commandSocket]} { error "remote server disappeared" } if {[catch {puts $commandSocket $c} msg]} { error "remote server disappaered: $msg" } if {[catch {puts $commandSocket "--Marker--Marker--Marker--"} msg]} { error "remote server disappeared: $msg" } while {1} { set line [gets $commandSocket] if {[eof $commandSocket]} { error "remote server disappaered" } if {$line eq "--Marker--Marker--Marker--"} { lassign $result code info value return -code $code -errorinfo $info $value } append result $line "\n" } } } proc getPort sock { lindex [fconfigure $sock -sockname] 2 } # ---------------------------------------------------------------------- test socket-1.1 {arg parsing for socket command} -constraints socket -body { socket -server } -returnCodes error -result {no argument given for -server option} test socket-1.2 {arg parsing for socket command} -constraints socket -body { socket -server foo } -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} test socket-1.3 {arg parsing for socket command} -constraints socket -body { socket -myaddr } -returnCodes error -result {no argument given for -myaddr option} test socket-1.4 {arg parsing for socket command} -constraints socket -body { socket -myaddr 127.0.0.1 } -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} test socket-1.5 {arg parsing for socket command} -constraints socket -body { socket -myport } -returnCodes error -result {no argument given for -myport option} test socket-1.6 {arg parsing for socket command} -constraints socket -body { socket -myport xxxx } -returnCodes error -result {expected integer but got "xxxx"} test socket-1.7 {arg parsing for socket command} -constraints socket -body { socket -myport 2522 } -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} test socket-1.8 {arg parsing for socket command} -constraints socket -body { socket -froboz } -returnCodes error -result {bad option "-froboz": must be -async, -myaddr, -myport, or -server} test socket-1.9 {arg parsing for socket command} -constraints socket -body { socket -server foo -myport 2521 3333 } -returnCodes error -result {option -myport is not valid for servers} test socket-1.10 {arg parsing for socket command} -constraints socket -body { socket host 2528 -junk } -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} test socket-1.11 {arg parsing for socket command} -constraints socket -body { socket -server callback 2520 -- } -returnCodes error -result {wrong # args: should be "socket ?-myaddr addr? ?-myport myport? ?-async? host port" or "socket -server command ?-myaddr addr? port"} test socket-1.12 {arg parsing for socket command} -constraints socket -body { socket foo badport } -returnCodes error -result {expected integer but got "badport"} test socket-1.13 {arg parsing for socket command} -constraints socket -body { socket -async -server } -returnCodes error -result {cannot set -async option for server sockets} test socket-1.14 {arg parsing for socket command} -constraints socket -body { socket -server foo -async } -returnCodes error -result {cannot set -async option for server sockets} set path(script) [makeFile {} script] test socket-2.1 {tcp connection} -constraints {socket stdio} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timed_out"] set f [socket -server accept 0] proc accept {file addr port} { global x set x done close $file } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f puts $x } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -body { # $x == "ready" at this point set sock [socket 127.0.0.1 $listen] lappend x [gets $f] close $sock lappend x [gets $f] } -cleanup { close $f } -result {ready done {}} test socket-2.2 {tcp connection with client port specified} -setup { set port [randport] file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timeout"] set f [socket -server accept 0] proc accept {file addr port} { global x puts "[gets $file] $port" close $file set x done } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -constraints {socket stdio} -body { # $x == "ready" at this point set sock [socket -myport $port 127.0.0.1 $listen] puts $sock hello flush $sock lappend x [expr {[gets $f] eq "hello $port"}] close $sock return $x } -cleanup { catch {close [socket 127.0.0.1 $listen]} close $f } -result {ready 1} test socket-2.3 {tcp connection with client interface specified} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 2000 "set x done"] set f [socket -server accept 0] proc accept {file addr port} { global x puts "[gets $file] $addr" close $file set x done } puts [lindex [fconfigure $f -sockname] 2] puts ready vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f listen gets $f x } -constraints {socket stdio} -body { # $x == "ready" at this point set sock [socket -myaddr 127.0.0.1 127.0.0.1 $listen] puts $sock hello flush $sock lappend x [gets $f] close $sock return $x } -cleanup { close $f } -result {ready {hello 127.0.0.1}} test socket-2.4 {tcp connection with server interface specified} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 2000 "set x done"] set f [socket -server accept -myaddr 127.0.0.1 0] proc accept {file addr port} { global x puts "[gets $file]" close $file set x done } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -constraints {socket stdio} -body { # $x == "ready" at this point set sock [socket 127.0.0.1 $listen] puts $sock hello flush $sock lappend x [gets $f] close $sock return $x } -cleanup { close $f } -result {ready hello} test socket-2.5 {tcp connection with redundant server port} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timeout"] set f [socket -server accept 0] proc accept {file addr port} { global x puts "[gets $file]" close $file set x done } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f x gets $f listen } -constraints {socket stdio} -body { # $x == "ready" at this point set sock [socket 127.0.0.1 $listen] puts $sock hello flush $sock lappend x [gets $f] close $sock return $x } -cleanup { close $f } -result {ready hello} test socket-2.6 {tcp connection} -constraints socket -body { set status ok if {![catch {set sock [socket 127.0.0.1 [randport]]}]} { if {![catch {gets $sock}]} { set status broken } close $sock } set status } -result ok test socket-2.7 {echo server, one line} -constraints {socket stdio} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set timer [after 10000 "set x timeout"] set f [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -translation lf -buffering line } proc echo {s} { set l [gets $s] if {[eof $s]} { global x close $s set x done } else { puts $s $l } } puts ready puts [lindex [fconfigure $f -sockname] 2] vwait x after cancel $timer close $f puts $x } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen } -body { set s [socket 127.0.0.1 $listen] fconfigure $s -buffering line -translation lf puts $s "hello abcdefghijklmnop" after 1000 set x [gets $s] close $s list $x [gets $f] } -cleanup { close $f } -result {{hello abcdefghijklmnop} done} removeFile script test socket-2.8 {echo server, loop 50 times, single connection} -setup { set path(script) [makeFile { set f [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line } proc echo {s} { global i set l [gets $s] if {[eof $s]} { global x close $s set x done } else { incr i puts $s $l } } set i 0 puts ready puts [lindex [fconfigure $f -sockname] 2] set timer [after 20000 "set x done"] vwait x after cancel $timer close $f puts "done $i" } script] set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen } -constraints {socket stdio} -body { set s [socket 127.0.0.1 $listen] fconfigure $s -buffering line catch { for {set x 0} {$x < 50} {incr x} { puts $s "hello abcdefghijklmnop" gets $s } } close $s catch {set x [gets $f]} return $x } -cleanup { close $f removeFile script } -result {done 50} set path(script) [makeFile {} script] test socket-2.9 {socket conflict} -constraints {socket stdio} -body { set s [socket -server accept 0] file delete $path(script) set f [open $path(script) w] puts -nonewline $f "socket -server accept [lindex [fconfigure $s -sockname] 2]" close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f after 100 close $f } -returnCodes error -cleanup { close $s } -match glob -result {couldn't open socket: address already in use*} test socket-2.10 {close on accept, accepted socket lives} -setup { set done 0 set timer [after 20000 "set done timed_out"] } -constraints socket -body { set ss [socket -server accept 0] proc accept {s a p} { global ss close $ss fileevent $s readable "readit $s" fconfigure $s -trans lf } proc readit {s} { global done gets $s close $s set done 1 } set cs [socket [info hostname] [lindex [fconfigure $ss -sockname] 2]] puts $cs hello close $cs vwait done return $done } -cleanup { after cancel $timer } -result 1 test socket-2.11 {detecting new data} -constraints socket -setup { proc accept {s a p} { global sock set sock $s } set s [socket -server accept 0] set sock "" } -body { set s2 [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]] vwait sock puts $s2 one flush $s2 after 500 fconfigure $sock -blocking 0 set result a:[gets $sock] lappend result b:[gets $sock] fconfigure $sock -blocking 1 puts $s2 two flush $s2 after 500 fconfigure $sock -blocking 0 lappend result c:[gets $sock] } -cleanup { fconfigure $sock -blocking 1 close $s2 close $s close $sock } -result {a:one b: c:two} test socket-3.1 {socket conflict} -constraints {socket stdio} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set f [socket -server accept -myaddr 127.0.0.1 0] puts ready puts [lindex [fconfigure $f -sockname] 2] gets stdin close $f } close $f set f [open "|[list [interpreter] $path(script)]" r+] gets $f gets $f listen } -body { socket -server accept -myaddr 127.0.0.1 $listen } -cleanup { puts $f bye close $f } -returnCodes error -result {couldn't open socket: address already in use} test socket-3.2 {server with several clients} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set t1 [after 30000 "set x timed_out"] set t2 [after 31000 "set x timed_out"] set t3 [after 32000 "set x timed_out"] set counter 0 set s [socket -server accept -myaddr 127.0.0.1 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line } proc echo {s} { global x set l [gets $s] if {[eof $s]} { close $s set x done } else { puts $s $l } } puts ready puts [lindex [fconfigure $s -sockname] 2] vwait x after cancel $t1 vwait x after cancel $t2 vwait x after cancel $t3 close $s puts $x } close $f set f [open "|[list [interpreter] $path(script)]" r+] set x [gets $f] gets $f listen } -constraints {socket stdio} -body { # $x == "ready" here set s1 [socket 127.0.0.1 $listen] fconfigure $s1 -buffering line set s2 [socket 127.0.0.1 $listen] fconfigure $s2 -buffering line set s3 [socket 127.0.0.1 $listen] fconfigure $s3 -buffering line for {set i 0} {$i < 100} {incr i} { puts $s1 hello,s1 gets $s1 puts $s2 hello,s2 gets $s2 puts $s3 hello,s3 gets $s3 } close $s1 close $s2 close $s3 lappend x [gets $f] } -cleanup { close $f } -result {ready done} test socket-4.1 {server with several clients} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set port [gets stdin] set s [socket 127.0.0.1 $port] fconfigure $s -buffering line for {set i 0} {$i < 100} {incr i} { puts $s hello gets $s } close $s puts bye gets stdin } close $f set p1 [open "|[list [interpreter] $path(script)]" r+] fconfigure $p1 -buffering line set p2 [open "|[list [interpreter] $path(script)]" r+] fconfigure $p2 -buffering line set p3 [open "|[list [interpreter] $path(script)]" r+] fconfigure $p3 -buffering line } -constraints {socket stdio} -body { proc accept {s a p} { fconfigure $s -buffering line fileevent $s readable [list echo $s] } proc echo {s} { global x set l [gets $s] if {[eof $s]} { close $s set x done } else { puts $s $l } } set t1 [after 30000 "set x timed_out"] set t2 [after 31000 "set x timed_out"] set t3 [after 32000 "set x timed_out"] set s [socket -server accept -myaddr 127.0.0.1 0] set listen [lindex [fconfigure $s -sockname] 2] puts $p1 $listen puts $p2 $listen puts $p3 $listen vwait x vwait x vwait x after cancel $t1 after cancel $t2 after cancel $t3 close $s set l "" lappend l [list p1 [gets $p1] $x] lappend l [list p2 [gets $p2] $x] lappend l [list p3 [gets $p3] $x] } -cleanup { puts $p1 bye puts $p2 bye puts $p3 bye close $p1 close $p2 close $p3 } -result {{p1 bye done} {p2 bye done} {p3 bye done}} test socket-4.2 {byte order problems, socket numbers, htons} -body { close [socket -server dodo -myaddr 127.0.0.1 0x3000] return ok } -constraints socket -result ok test socket-5.1 {byte order problems, socket numbers, htons} -body { if {![catch {socket -server dodo 0x1} msg]} { close $msg return {htons problem, should be disallowed, are you running as SU?} } return {couldn't open socket: not owner} } -constraints {socket unix notRoot} -result {couldn't open socket: not owner} test socket-5.2 {byte order problems, socket numbers, htons} -body { if {![catch {socket -server dodo 0x10000} msg]} { close $msg return {port resolution problem, should be disallowed} } return {couldn't open socket: port number too high} } -constraints socket -result {couldn't open socket: port number too high} test socket-5.3 {byte order problems, socket numbers, htons} -body { if {![catch {socket -server dodo 21} msg]} { close $msg return {htons problem, should be disallowed, are you running as SU?} } return {couldn't open socket: not owner} } -constraints {socket unix notRoot} -result {couldn't open socket: not owner} test socket-6.1 {accept callback error} -constraints {socket stdio} -setup { proc myHandler {msg options} { variable x $msg } set handler [interp bgerror {}] interp bgerror {} [namespace which myHandler] file delete $path(script) } -body { set f [open $path(script) w] puts $f { gets stdin port socket 127.0.0.1 $port } close $f set f [open "|[list [interpreter] $path(script)]" r+] proc accept {s a p} {expr 10 / 0} set s [socket -server accept -myaddr 127.0.0.1 0] puts $f [lindex [fconfigure $s -sockname] 2] close $f set timer [after 10000 "set x timed_out"] vwait x after cancel $timer close $s return $x } -cleanup { interp bgerror {} $handler } -result {divide by zero} test socket-7.1 {testing socket specific options} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set ss [socket -server accept 0] proc accept args { global x set x done } puts ready puts [lindex [fconfigure $ss -sockname] 2] set timer [after 10000 "set x timed_out"] vwait x after cancel $timer } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen set l "" } -constraints {socket stdio} -body { set s [socket 127.0.0.1 $listen] set p [fconfigure $s -peername] close $s lappend l [string compare [lindex $p 0] 127.0.0.1] lappend l [string compare [lindex $p 2] $listen] lappend l [llength $p] } -cleanup { close $f } -result {0 0 3} test socket-7.2 {testing socket specific options} -setup { file delete $path(script) set f [open $path(script) w] puts $f { set ss [socket -server accept 0] proc accept args { global x set x done } puts ready puts [lindex [fconfigure $ss -sockname] 2] set timer [after 10000 "set x timed_out"] vwait x after cancel $timer } close $f set f [open "|[list [interpreter] $path(script)]" r] gets $f gets $f listen } -constraints {socket stdio} -body { set s [socket 127.0.0.1 $listen] set p [fconfigure $s -sockname] close $s list [llength $p] \ [regexp {^(127\.0\.0\.1|0\.0\.0\.0)$} [lindex $p 0]] \ [expr {[lindex $p 2] == $listen}] } -cleanup { close $f } -result {3 1 0} test socket-7.3 {testing socket specific options} -constraints socket -body { set s [socket -server accept -myaddr 127.0.0.1 0] set l [fconfigure $s] close $s update llength $l } -result 14 test socket-7.4 {testing socket specific options} -constraints socket -setup { set timer [after 10000 "set x timed_out"] set l "" } -body { set s [socket -server accept -myaddr 127.0.0.1 0] proc accept {s a p} { global x set x [fconfigure $s -sockname] close $s } set listen [lindex [fconfigure $s -sockname] 2] set s1 [socket 127.0.0.1 $listen] vwait x lappend l [expr {[lindex $x 2] == $listen}] [llength $x] } -cleanup { after cancel $timer close $s close $s1 } -result {1 3} test socket-7.5 {testing socket specific options} -setup { set timer [after 10000 "set x timed_out"] set l "" } -constraints {socket unixOrPc} -body { set s [socket -server accept 0] proc accept {s a p} { global x set x [fconfigure $s -sockname] close $s } set listen [lindex [fconfigure $s -sockname] 2] set s1 [socket 127.0.0.1 $listen] vwait x lappend l [lindex $x 0] [expr {[lindex $x 2] == $listen}] [llength $x] } -cleanup { after cancel $timer close $s close $s1 } -result {127.0.0.1 1 3} test socket-8.1 {testing -async flag on sockets} -constraints socket -body { # NOTE: This test may fail on some Solaris 2.4 systems. If it does, check # that you have these patches installed (using showrev -p): # # 101907-05, 101925-02, 101945-14, 101959-03, 101969-05, 101973-03, # 101977-03, 101981-02, 101985-01, 102001-03, 102003-01, 102007-01, # 102011-02, 102024-01, 102039-01, 102044-01, 102048-01, 102062-03, # 102066-04, 102070-01, 102105-01, 102153-03, 102216-01, 102232-01, # 101878-03, 101879-01, 101880-03, 101933-01, 101950-01, 102030-01, # 102057-08, 102140-01, 101920-02, 101921-09, 101922-07, 101923-03 # # If after installing these patches you are still experiencing a problem, # please email jyl@eng.sun.com. We have not observed this failure on # Solaris 2.5, so another option (instead of installing these patches) is # to upgrade to Solaris 2.5. set s [socket -server accept -myaddr 127.0.0.1 0] proc accept {s a p} { global x puts $s bye close $s set x done } set s1 [socket -async 127.0.0.1 [lindex [fconfigure $s -sockname] 2]] vwait x gets $s1 } -cleanup { close $s close $s1 } -result bye test socket-9.1 {testing spurious events} -constraints socket -setup { set len 0 set spurious 0 set done 0 set timer [after 10000 "set done timed_out"] } -body { proc readlittle {s} { global spurious done len set l [read $s 1] if {[string length $l] == 0} { if {![eof $s]} { incr spurious } else { close $s set done 1 } } else { incr len [string length $l] } } proc accept {s a p} { fconfigure $s -buffering none -blocking off fileevent $s readable [list readlittle $s] } set s [socket -server accept -myaddr 127.0.0.1 0] set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]] puts -nonewline $c 01234567890123456789012345678901234567890123456789 close $c vwait done close $s list $spurious $len } -cleanup { after cancel $timer } -result {0 50} test socket-9.2 {testing async write, fileevents, flush on close} -constraints socket -setup { set firstblock "" for {set i 0} {$i < 5} {incr i} {set firstblock "a$firstblock$firstblock"} set secondblock "" for {set i 0} {$i < 16} {incr i} { set secondblock "b$secondblock$secondblock" } set timer [after 10000 "set done timed_out"] set l [socket -server accept -myaddr 127.0.0.1 0] proc accept {s a p} { fconfigure $s -blocking 0 -translation lf -buffersize 16384 \ -buffering line fileevent $s readable "readable $s" } proc readable {s} { set l [gets $s] fileevent $s readable {} after 1000 respond $s } proc respond {s} { global firstblock puts -nonewline $s $firstblock after 1000 writedata $s } proc writedata {s} { global secondblock puts -nonewline $s $secondblock close $s } } -body { set s [socket 127.0.0.1 [lindex [fconfigure $l -sockname] 2]] fconfigure $s -blocking 0 -trans lf -buffering line set count 0 puts $s hello proc readit {s} { global count done set l [read $s] incr count [string length $l] if {[eof $s]} { close $s set done 1 } } fileevent $s readable "readit $s" vwait done return $count } -cleanup { close $l after cancel $timer } -result 65566 test socket-9.3 {testing EOF stickyness} -constraints socket -setup { set count 0 set done false proc write_then_close {s} { puts $s bye close $s } proc accept {s a p} { fconfigure $s -buffering line -translation lf fileevent $s writable "write_then_close $s" } set s [socket -server accept -myaddr 127.0.0.1 0] } -body { proc count_to_eof {s} { global count done set l [gets $s] if {[eof $s]} { incr count if {$count > 9} { close $s set done true set count {eof is sticky} } } } proc timerproc {s} { global done count set done true set count {timer went off, eof is not sticky} close $s } set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]] fconfigure $c -blocking off -buffering line -translation lf fileevent $c readable "count_to_eof $c" set timer [after 1000 timerproc $c] vwait done return $count } -cleanup { close $s after cancel $timer } -result {eof is sticky} removeFile script test socket-10.1 {testing socket accept callback error handling} -constraints { socket } -setup { variable goterror 0 proc myHandler {msg options} { variable goterror 1 } set handler [interp bgerror {}] interp bgerror {} [namespace which myHandler] } -body { set s [socket -server accept -myaddr 127.0.0.1 0] proc accept {s a p} {close $s; error} set c [socket 127.0.0.1 [lindex [fconfigure $s -sockname] 2]] vwait goterror close $s close $c return $goterror } -cleanup { interp bgerror {} $handler } -result 1 test socket-11.1 {tcp connection} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { puts $s done close $s } getPort $server }] } -constraints {socket doTestsWithRemoteServer} -body { set s [socket $remoteServerIP $port] gets $s } -cleanup { close $s sendCommand {close $server} } -result done test socket-11.2 {client specifies its port} -setup { set lport [randport] set rport [sendCommand { set server [socket -server accept 0] proc accept {s a p} { puts $s $p close $s } getPort $server }] } -constraints {socket doTestsWithRemoteServer} -body { set s [socket -myport $lport $remoteServerIP $rport] set r [gets $s] expr {$r==$lport ? "ok" : "broken: $r != $port"} } -cleanup { close $s sendCommand {close $server} } -result ok test socket-11.3 {trying to connect, no server} -body { set status ok if {![catch {set s [socket $remoteServerIp [randport]]}]} { if {![catch {gets $s}]} { set status broken } close $s } return $status } -constraints {socket doTestsWithRemoteServer} -result ok test socket-11.4 {remote echo, one line} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line -translation crlf } proc echo {s} { set l [gets $s] if {[eof $s]} { close $s } else { puts $s $l } } getPort $server }] } -constraints {socket doTestsWithRemoteServer} -body { set f [socket $remoteServerIP $port] fconfigure $f -translation crlf -buffering line puts $f hello gets $f } -cleanup { catch {close $f} sendCommand {close $server} } -result hello test socket-11.5 {remote echo, 50 lines} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line -translation crlf } proc echo {s} { set l [gets $s] if {[eof $s]} { close $s } else { puts $s $l } } getPort $server }] } -constraints {socket doTestsWithRemoteServer} -body { set f [socket $remoteServerIP $port] fconfigure $f -translation crlf -buffering line for {set cnt 0} {$cnt < 50} {incr cnt} { puts $f "hello, $cnt" if {[gets $f] != "hello, $cnt"} { break } } return $cnt } -cleanup { close $f sendCommand {close $server} } -result 50 test socket-11.6 {socket conflict} -setup { set s1 [socket -server accept -myaddr 127.0.0.1 0] } -constraints {socket doTestsWithRemoteServer} -body { set s2 [socket -server accept -myaddr 127.0.0.1 [getPort $s1]] list [getPort $s2] [close $s2] } -cleanup { close $s1 } -returnCodes error -result {couldn't open socket: address already in use} test socket-11.7 {server with several clients} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fconfigure $s -buffering line fileevent $s readable [list echo $s] } proc echo {s} { set l [gets $s] if {[eof $s]} { close $s } else { puts $s $l } } getPort $server }] } -constraints {socket doTestsWithRemoteServer} -body { set s1 [socket $remoteServerIP $port] fconfigure $s1 -buffering line set s2 [socket $remoteServerIP $port] fconfigure $s2 -buffering line set s3 [socket $remoteServerIP $port] fconfigure $s3 -buffering line for {set i 0} {$i < 100} {incr i} { puts $s1 hello,s1 gets $s1 puts $s2 hello,s2 gets $s2 puts $s3 hello,s3 gets $s3 } return $i } -cleanup { close $s1 close $s2 close $s3 sendCommand {close $server} } -result 100 test socket-11.8 {client with several servers} -setup { lassign [sendCommand { set s1 [socket -server "accept server1" 0] set s2 [socket -server "accept server2" 0] set s3 [socket -server "accept server3" 0] proc accept {mp s a p} { puts $s $mp close $s } list [getPort $s1] [getPort $s2] [getPort $s3] }] p1 p2 p3 } -constraints {socket doTestsWithRemoteServer} -body { set s1 [socket $remoteServerIP $p1] set s2 [socket $remoteServerIP $p2] set s3 [socket $remoteServerIP $p3] list [gets $s1] [gets $s1] [eof $s1] [gets $s2] [gets $s2] [eof $s2] \ [gets $s3] [gets $s3] [eof $s3] } -cleanup { close $s1 close $s2 close $s3 sendCommand { close $s1 close $s2 close $s3 } } -result {server1 {} 1 server2 {} 1 server3 {} 1} test socket-11.9 {accept callback error} -constraints { socket doTestsWithRemoteServer } -setup { proc myHandler {msg options} { variable x $msg } set handler [interp bgerror {}] interp bgerror {} [namespace which myHandler] set timer [after 10000 "set x timed_out"] } -body { set s [socket -server accept 0] proc accept {s a p} {expr {10 / 0}} sendCommand "set port [getPort $s]" if {[catch { sendCommand { set peername [fconfigure $callerSocket -peername] set s [socket [lindex $peername 0] $port] close $s } } msg]} then { close $s error $msg } vwait x return $x } -cleanup { close $s after cancel $timer interp bgerror {} $handler } -result {divide by zero} test socket-11.10 {testing socket specific options} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} {close $s} getPort $server }] } -constraints {socket doTestsWithRemoteServer} -body { set s [socket $remoteServerIP $port] set p [fconfigure $s -peername] set n [fconfigure $s -sockname] list [expr {[lindex $p 2] == $port}] [llength $p] [llength $n] } -cleanup { close $s sendCommand {close $server} } -result {1 3 3} test socket-11.11 {testing spurious events} -setup { set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { fconfigure $s -translation "auto lf" after 100 writesome $s } proc writesome {s} { for {set i 0} {$i < 100} {incr i} { puts $s "line $i from remote server" } close $s } getPort $server }] set len 0 set spurious 0 set done 0 set timer [after 40000 "set done timed_out"] } -constraints {socket doTestsWithRemoteServer} -body { proc readlittle {s} { global spurious done len set l [read $s 1] if {[string length $l] == 0} { if {![eof $s]} { incr spurious } else { close $s set done 1 } } else { incr len [string length $l] } } set c [socket $remoteServerIP $port] fileevent $c readable "readlittle $c" vwait done list $spurious $len $done } -cleanup { after cancel $timer sendCommand {close $server} } -result {0 2690 1} test socket-11.12 {testing EOF stickyness} -constraints {socket doTestsWithRemoteServer} -setup { set counter 0 set done 0 set port [sendCommand { set server [socket -server accept 0] proc accept {s a p} { after 100 close $s } getPort $server }] proc timed_out {} { global c done set done {timed_out, EOF is not sticky} close $c } set after_id [after 1000 timed_out] } -body { proc count_up {s} { global counter done set l [gets $s] if {[eof $s]} { incr counter if {$counter > 9} { set done {EOF is sticky} close $s } } } set c [socket $remoteServerIP $port] fileevent $c readable [list count_up $c] vwait done return $done } -cleanup { after cancel $after_id sendCommand {close $server} } -result {EOF is sticky} test socket-11.13 {testing async write, async flush, async close} -setup { set port [sendCommand { set firstblock "" for {set i 0} {$i < 5} {incr i} { set firstblock "a$firstblock$firstblock" } set secondblock "" for {set i 0} {$i < 16} {incr i} { set secondblock "b$secondblock$secondblock" } set l [socket -server accept 0] proc accept {s a p} { fconfigure $s -blocking 0 -translation lf -buffersize 16384 \ -buffering line fileevent $s readable "readable $s" } proc readable {s} { set l [gets $s] fileevent $s readable {} after 1000 respond $s } proc respond {s} { global firstblock puts -nonewline $s $firstblock after 1000 writedata $s } proc writedata {s} { global secondblock puts -nonewline $s $secondblock close $s } getPort $l }] set timer [after 10000 "set done timed_out"] } -constraints {socket doTestsWithRemoteServer} -body { proc readit {s} { global count done set l [read $s] incr count [string length $l] if {[eof $s]} { close $s set done 1 } } set s [socket $remoteServerIP $port] fconfigure $s -blocking 0 -trans lf -buffering line set count 0 puts $s hello fileevent $s readable "readit $s" vwait done return $count } -cleanup { after cancel $timer sendCommand {close $l} } -result 65566 set path(script1) [makeFile {} script1] set path(script2) [makeFile {} script2] test socket-12.1 {testing inheritance of server sockets} -setup { file delete $path(script1) file delete $path(script2) # Script1 is just a 10 second delay. If the server socket is inherited, it # will be held open for 10 seconds set f [open $path(script1) w] puts $f { after 10000 exit vwait forever } close $f # Script2 creates the server socket, launches script1, waits a second, and # exits. The server socket will now be closed unless script1 inherited it. set f [open $path(script2) w] puts $f [list set tcltest [interpreter]] puts $f [list set delay $path(script1)] puts $f { set f [socket -server accept -myaddr 127.0.0.1 0] puts [lindex [fconfigure $f -sockname] 2] proc accept { file addr port } { close $file } exec $tcltest $delay & close $f after 1000 exit vwait forever } close $f } -constraints {socket stdio exec} -body { # Launch script2 and wait 5 seconds ### exec [interpreter] script2 & set p [open "|[list [interpreter] $path(script2)]" r] gets $p listen after 5000 { set ok_to_proceed 1 } vwait ok_to_proceed # If we can still connect to the server, the socket got inherited. if {[catch {close [socket 127.0.0.1 $listen]}]} { return {server socket was not inherited} } else { return {server socket was inherited} } } -cleanup { close $p } -result {server socket was not inherited} test socket-12.2 {testing inheritance of client sockets} -setup { file delete $path(script1) file delete $path(script2) # Script1 is just a 20 second delay. If the server socket is inherited, it # will be held open for 20 seconds set f [open $path(script1) w] puts $f { after 20000 exit vwait forever } close $f # Script2 opens the client socket and writes to it. It then launches # script1 and exits. If the child process inherited the client socket, the # socket will still be open. set f [open $path(script2) w] puts $f [list set tcltest [interpreter]] puts $f [list set delay $path(script1)] puts $f { gets stdin port set f [socket 127.0.0.1 $port] exec $tcltest $delay & puts $f testing flush $f after 1000 exit vwait forever } close $f # If the socket doesn't hit end-of-file in 10 seconds, the script1 process # must have inherited the client. set failed 0 after 10000 [list set failed 1] } -constraints {socket stdio exec} -body { # Create the server socket set server [socket -server accept -myaddr 127.0.0.1 0] proc accept { file host port } { # When the client connects, establish the read handler global server close $server fileevent $file readable [list getdata $file] fconfigure $file -buffering line -blocking 0 } proc getdata { file } { # Read handler on the accepted socket. global x failed set status [catch {read $file} data] if {$status != 0} { set x {read failed, error was $data} catch { close $file } } elseif {$data ne ""} { } elseif {[fblocked $file]} { } elseif {[eof $file]} { if {$failed} { set x {client socket was inherited} } else { set x {client socket was not inherited} } catch { close $file } } else { set x {impossible case} catch { close $file } } } # Launch the script2 process ### exec [interpreter] script2 & set p [open "|[list [interpreter] $path(script2)]" w] puts $p [lindex [fconfigure $server -sockname] 2] ; flush $p vwait x return $x } -cleanup { if {!$failed} { vwait failed } close $p } -result {client socket was not inherited} test socket-12.3 {testing inheritance of accepted sockets} -setup { file delete $path(script1) file delete $path(script2) set f [open $path(script1) w] puts $f { after 10000 exit vwait forever } close $f set f [open $path(script2) w] puts $f [list set tcltest [interpreter]] puts $f [list set delay $path(script1)] puts $f { set server [socket -server accept -myaddr 127.0.0.1 0] puts stdout [lindex [fconfigure $server -sockname] 2] proc accept { file host port } { global tcltest delay puts $file {test data on socket} exec $tcltest $delay & after 1000 exit } vwait forever } close $f } -constraints {socket stdio exec} -body { # Launch the script2 process and connect to it. See how long the socket # stays open ## exec [interpreter] script2 & set p [open "|[list [interpreter] $path(script2)]" r] gets $p listen after 1000 set ok_to_proceed 1 vwait ok_to_proceed set f [socket 127.0.0.1 $listen] fconfigure $f -buffering full -blocking 0 fileevent $f readable [list getdata $f] # If the socket is still open after 5 seconds, the script1 process must # have inherited the accepted socket. set failed 0 after 5000 set failed 1 proc getdata { file } { # Read handler on the client socket. global x global failed set status [catch {read $file} data] if {$status != 0} { set x {read failed, error was $data} catch { close $file } } elseif {[string compare {} $data]} { } elseif {[fblocked $file]} { } elseif {[eof $file]} { if {$failed} { set x {accepted socket was inherited} } else { set x {accepted socket was not inherited} } catch { close $file } } else { set x {impossible case} catch { close $file } } return } vwait x return $x } -cleanup { catch {close $p} } -result {accepted socket was not inherited} test socket-13.1 {Testing use of shared socket between two threads} -setup { threadReap set path(script) [makeFile { set f [socket -server accept -myaddr 127.0.0.1 0] set listen [lindex [fconfigure $f -sockname] 2] proc accept {s a p} { fileevent $s readable [list echo $s] fconfigure $s -buffering line } proc echo {s} { global i set l [gets $s] if {[eof $s]} { global x close $s set x done } else { incr i puts $s $l } } set i 0 vwait x close $f # thread cleans itself up. testthread exit } script] } -constraints {socket testthread} -body { # create a thread set serverthread [testthread create [list source $path(script) ] ] update set port [testthread send $serverthread {set listen}] update after 1000 set s [socket 127.0.0.1 $port] fconfigure $s -buffering line catch { puts $s "hello" gets $s result } close $s update after 2000 append result " " [threadReap] } -cleanup { removeFile script } -result {hello 1} # ---------------------------------------------------------------------- removeFile script1 removeFile script2 # cleanup if {$remoteProcChan ne ""} { catch {sendCommand exit} } catch {close $commandSocket} catch {close $remoteProcChan} ::tcltest::cleanupTests flush stdout return # Local Variables: # mode: tcl # fill-column: 78 # End: