# 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 © 1994-1996 Sun Microsystems, Inc.
# Copyright © 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.

# 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 <name or address of machine on which server runs>
#     % 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.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

::tcltest::loadTestedCommands
catch [list package require -exact tcl::test [info patchlevel]]
::tcltest::loadTestedCommands

# A bad interaction between socket creation, macOS, and unattended CI
# environments make this whole file impractical to run; too many weird hangs.
if {[info exists ::env(MAC_CI)]} {
    return
}
testConstraint notWine [expr {![info exists ::env(CI_USING_WINE)]}]

# Some tests require the Thread package or exec command
testConstraint thread [expr {0 == [catch {package require Thread 2.7-}]}]
testConstraint exec [llength [info commands exec]]
testConstraint notWinCI [expr {
     $tcl_platform(platform) ne "windows" || ![info exists ::env(CI)]}]

# Produce a random port number in the Dynamic/Private range
# from 49152 through 65535.
proc randport {} {
    # firstly try dynamic port via server-socket(0):
    set port 0x7fffffff
    catch {
	set port [lindex [fconfigure [set s [socket -server {} 0]] -sockname] 2]
	close $s
    }
    while {[catch {
	close [socket -server {} $port]
    } msg]} {
	if {[incr i] > 1000} {return -code error "too many iterations to get free random port: $msg"}
	# try random port:
	set port [expr {int(rand()*16383+49152)}]
    }
    return $port
}

# Check if testsocket testflags is available
testConstraint testsocket_testflags [expr {![catch {
	set h [socket -async localhost [randport]]
	testsocket testflags $h 0
	close $h
    }]}]


# Test the latency of tcp connections over the loopback interface. Some OSes
# (e.g. NetBSD) seem to use the Nagle algorithm and delayed ACKs, so it takes
# up to 200ms for a packet sent to localhost to arrive. We're measuring this
# here, so that OSes that don't have this problem can run the tests at full
# speed.
set server [socket -server {apply {{s a p} {set ::s1 $s}}} 0]
set s2 [socket localhost [lindex [fconfigure $server -sockname] 2]]
vwait s1; close $server
fconfigure $s1 -buffering line
fconfigure $s2 -buffering line
set t1 [clock milliseconds]
puts $s2 test1; gets $s1
puts $s2 test2; gets $s1
close $s1; close $s2
set t2 [clock milliseconds]
set lat1 [expr {($t2-$t1)*2}]; # doubled as a safety margin

# Test the latency of failed connection attempts over the loopback
# interface. They can take more than a second under Windows and requires
# additional [after]s in some tests that are not needed on systems that fail
# immediately.
set t1 [clock milliseconds]
catch {socket 127.0.0.1 [randport]}
set t2 [clock milliseconds]
set lat2 [expr {($t2-$t1)*3}]

# Use the maximum of the two latency calculations, but at least 200ms
set latency [expr {$lat1 > $lat2 ? $lat1 : $lat2}]
set latency [expr {$latency > 200 ? $latency : 200}]
unset t1 t2 s1 s2 lat1 lat2 server

# 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
	}
    }
}

if 0 {
    # activate this to time the tests
    proc test {args} {
	set name [lindex $args 0]
	puts "[lindex [time {uplevel [linsert $args 0 tcltest::test]}] 0] @@@ $name"
    }
}

foreach {af localhost} {
    inet 127.0.0.1
    inet6 ::1
} {
    # Check if the family is supported and set the constraint accordingly
    testConstraint supported_$af [expr {![catch {socket -server foo -myaddr $localhost 0} sock]}]
    catch {close $sock}
}

set sock [socket -server foo -myaddr localhost 0]
set sockname [fconfigure $sock -sockname]
close $sock
testConstraint localhost_v4 [expr {"127.0.0.1" in $sockname}]
testConstraint localhost_v6 [expr {"::1" in $sockname}]


foreach {af localhost} {
    any 127.0.0.1
    inet 127.0.0.1
    inet6 ::1
} {
    if {![testConstraint supported_$af]} {
	continue
    }
    set ::tcl::unsupported::socketAF $af
#
# Check if we're supposed to do tests against the remote server
#

set doTestsWithRemoteServer 1
if {![info exists remoteServerIP]} {
    set remoteServerIP $localhost
}
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 $localhost
	# 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 {
	    gets $remoteProcChan
	    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] >= 0} {
	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
}

# Some tests in this file are known to hang *occasionally* on OSX; stop the
# worst offenders.
testConstraint notOSX [expr {$::tcl_platform(os) ne "Darwin"}]
# Here "Windows" means derived platforms as Cygwin or Msys2 too.
testConstraint notWindows [expr {![regexp {^(Windows|MSYS|CYGWIN)} $::tcl_platform(os)]}]

# ----------------------------------------------------------------------

test socket_$af-1.1 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -server
} -returnCodes error -result {no argument given for -server option}
test socket_$af-1.2 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -server foo
} -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"}
test socket_$af-1.3 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -myaddr
} -returnCodes error -result {no argument given for -myaddr option}
test socket_$af-1.4 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -myaddr $localhost
} -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"}
test socket_$af-1.5 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -myport
} -returnCodes error -result {no argument given for -myport option}
test socket_$af-1.6 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -myport xxxx
} -returnCodes error -result {expected integer but got "xxxx"}
test socket_$af-1.7 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -myport 2522
} -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"}
test socket_$af-1.8 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -froboz
} -returnCodes error -result {bad option "-froboz": must be -async, -backlog, -myaddr, -myport, -reuseaddr, -reuseport, or -server}
test socket_$af-1.9 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -server foo -myport 2521 3333
} -returnCodes error -result {option -myport is not valid for servers}
test socket_$af-1.10 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket host 2528 -junk
} -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"}
test socket_$af-1.11 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -server callback 2520 --
} -returnCodes error -result {wrong # args: should be "socket ?-async? ?-myaddr addr? ?-myport myport? host port" or "socket -server command ?-backlog count? ?-myaddr addr? ?-reuseaddr boolean? ?-reuseport boolean? port"}
test socket_$af-1.12 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket foo badport
} -returnCodes error -result {expected integer but got "badport"}
test socket_$af-1.13 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -async -server
} -returnCodes error -result {cannot set -async option for server sockets}
test socket_$af-1.14 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -server foo -async
} -returnCodes error -result {cannot set -async option for server sockets}
test socket_$af-1.15 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -reuseaddr yes 4242
} -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers}
test socket_$af-1.16 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -reuseaddr no 4242
} -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers}
test socket_$af-1.17 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -reuseaddr
} -returnCodes error -result {no argument given for -reuseaddr option}
test socket_$af-1.18 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -reuseport yes 4242
} -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers}
test socket_$af-1.19 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -reuseport no 4242
} -returnCodes error -result {options -backlog, -reuseaddr, and -reuseport are only valid for servers}
test socket_$af-1.20 {arg parsing for socket command} -constraints [list socket supported_$af] -body {
    socket -reuseport
} -returnCodes error -result {no argument given for -reuseport option}

set path(script) [makeFile {} script]

test socket_$af-2.1 {tcp connection} -constraints [list socket supported_$af 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 $localhost $listen]
    lappend x [gets $f]
    close $sock
    lappend x [gets $f]
} -cleanup {
    close $f
} -result {ready done {}}
test socket_$af-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 [list socket supported_$af stdio] -body {
    # $x == "ready" at this point
    set sock [socket -myport $port $localhost $listen]
    puts $sock hello
    flush $sock
    lappend x [expr {[gets $f] eq "hello $port"}]
    close $sock
    return $x
} -cleanup {
    catch {close [socket $localhost $listen]}
    close $f
} -result {ready 1}
test socket_$af-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 [list socket supported_$af stdio] -body {
    # $x == "ready" at this point
    set sock [socket -myaddr $localhost $localhost $listen]
    puts $sock hello
    flush $sock
    lappend x [gets $f]
    close $sock
    return $x
} -cleanup {
    close $f
} -result [list ready [list hello $localhost]]
test socket_$af-2.4 {tcp connection with server interface specified} -setup {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f [list set localhost $localhost]
    puts $f {
	set timer [after 2000 "set x done"]
	set f [socket -server accept -myaddr $localhost 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 [list socket supported_$af stdio] -body {
    # $x == "ready" at this point
    set sock [socket $localhost $listen]
    puts $sock hello
    flush $sock
    lappend x [gets $f]
    close $sock
    return $x
} -cleanup {
    close $f
} -result {ready hello}
test socket_$af-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 [list socket supported_$af stdio] -body {
    # $x == "ready" at this point
    set sock [socket $localhost $listen]
    puts $sock hello
    flush $sock
    lappend x [gets $f]
    close $sock
    return $x
} -cleanup {
    close $f
} -result {ready hello}
test socket_$af-2.6 {tcp connection} -constraints [list socket supported_$af] -body {
    set status ok
    if {![catch {set sock [socket $localhost [randport]]}]} {
	if {![catch {gets $sock}]} {
	    set status broken
	}
	close $sock
    }
    set status
} -result ok
test socket_$af-2.7 {echo server, one line} -constraints [list socket supported_$af 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 $localhost $listen]
    fconfigure $s -buffering line -translation lf
    puts $s "hello abcdefghijklmnop"
    set x [gets $s]
    close $s
    list $x [gets $f]
} -cleanup {
    close $f
} -result {{hello abcdefghijklmnop} done}
removeFile script
test socket_$af-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 [list socket supported_$af stdio] -body {
    set s [socket $localhost $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_$af-2.9 {socket conflict} -constraints [list socket supported_$af stdio] -body {
    set s [socket -server accept 0]
    file delete $path(script)
    set f [open $path(script) w]
    puts $f [list set ::tcl::unsupported::socketAF $::tcl::unsupported::socketAF]
    puts $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_$af-2.10 {close on accept, accepted socket lives} -setup {
    set done 0
    set timer [after 20000 "set done timed_out"]
} -constraints [list socket supported_$af] -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 $localhost [lindex [fconfigure $ss -sockname] 2]]
    puts $cs hello
    close $cs
    vwait done
    return $done
} -cleanup {
    after cancel $timer
} -result 1
test socket_$af-2.11 {detecting new data} -constraints [list socket supported_$af] -setup {
    proc accept {s a p} {
	global sock
	set sock $s
    }
    set s [socket -server accept 0]
    set sock ""
} -body {
    set s2 [socket $localhost [lindex [fconfigure $s -sockname] 2]]
    vwait sock
    puts $s2 one
    flush $s2
    after $latency {set x 1}; # Spurious failures in Travis CI, if we do [after idle]
    vwait x
    fconfigure $sock -blocking 0
    set result a:[gets $sock]
    lappend result b:[gets $sock]
    fconfigure $sock -blocking 1
    puts $s2 two
    flush $s2
    after $latency {set x 1}; # NetBSD fails here if we do [after idle]
    vwait x
    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_$af-2.12 {} [list socket stdio supported_$af] {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f {
	set server [socket -server accept_client 0]
	puts [lindex [chan configure $server -sockname] 2]
	proc accept_client { client host port } {
	    chan configure $client -blocking  0 -buffering line
	    write_line $client
	}
	proc write_line client {
	    if { [catch { chan puts $client [string repeat . 720000]}] } {
		puts [catch {chan close $client}]
	    } else {
		puts signal1
		after 0 write_line $client
	    }
	}
	chan event stdin readable {set forever now}
	vwait forever
	exit
    }
    close $f
    set f [open "|[list [interpreter] $path(script)]" r+]
    gets $f port
    set sock [socket $localhost $port]
    chan event $sock readable [list read_lines $sock $f]
    proc read_lines { sock pipe } {
	gets $pipe
	chan close $sock
	chan event $pipe readable [list readpipe $pipe]
    }
    proc readpipe {pipe} {
	while {![string is integer [set ::done [gets $pipe]]]} {}
    }
    vwait ::done
    close $f
    set ::done
} 0
test socket_$af-2.13 {Bug 1758a0b603} {socket stdio notWine} {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f {
	set server [socket -server accept 0]
	puts [lindex [chan configure $server -sockname] 2]
	proc accept { client host port } {
	    chan configure $client -blocking  0 -buffering line -buffersize 1
	    puts $client [string repeat . 720000]
	    puts ready
	    chan event $client writable [list setup $client]
	}
	proc setup client {
	    chan event $client writable {set forever write}
	    after 5 {set forever timeout}
	}
	vwait forever
	puts $forever
    }
    close $f
    set pipe [open |[list [interpreter] $path(script)] r]
    gets $pipe port
    set sock [socket $localhost $port]
    chan configure $sock -blocking  0 -buffering line
    chan event $sock readable [list read_lines $sock $pipe ]
    proc read_lines { sock pipe } {
	gets $pipe
	gets $sock line
	after idle [list stop $sock $pipe]
	chan event $sock readable {}
    }
    proc stop {sock pipe} {
	variable done
	close $sock
	set done [gets $pipe]
    }
    variable done
    vwait [namespace which -variable done]
    close $pipe
    set done
} write

test socket_$af-3.1 {socket conflict} -constraints [list socket supported_$af stdio] -setup {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f [list set localhost $localhost]
    puts $f {
	set f [socket -server accept -myaddr $localhost 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 $localhost $listen
} -cleanup {
    puts $f bye
    close $f
} -returnCodes error -result {couldn't open socket: address already in use}
test socket_$af-3.2 {server with several clients} -setup {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f [list set localhost $localhost]
    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 $localhost 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 [list socket supported_$af stdio] -body {
    # $x == "ready" here
    set s1 [socket $localhost $listen]
    fconfigure $s1 -buffering line
    set s2 [socket $localhost $listen]
    fconfigure $s2 -buffering line
    set s3 [socket $localhost $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_$af-4.1 {server with several clients} -setup {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f [list set localhost $localhost]
    puts $f {
	set port [gets stdin]
	set s [socket $localhost $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 [list socket supported_$af 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 $localhost 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_$af-4.2 {byte order problems, socket numbers, htons} -body {
    close [socket -server dodo -myaddr $localhost 0x3000]
    return ok
} -constraints [list socket supported_$af] -result ok

test socket_$af-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 [list socket supported_$af unix notRoot notOSX notWindows] -result {couldn't open socket: not owner}
test socket_$af-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 [list socket supported_$af] -result {couldn't open socket: port number too high}
test socket_$af-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 [list socket supported_$af unix notRoot notOSX notWindows] -result {couldn't open socket: not owner}

test socket_$af-6.1 {accept callback error} -constraints [list socket supported_$af 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 [list set localhost $localhost]
    puts $f {
	gets stdin port
	socket $localhost $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 $localhost 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_$af-6.2 {
    readable fileevent on server socket
} -setup {
    set sock [socket -server dummy 0]
} -constraints [list socket supported_$af] -body {
    fileevent $sock readable dummy
} -cleanup {
    close $sock
} -returnCodes 1 -result "channel is not readable"

test socket_$af-6.3 {writable fileevent on server socket} -setup {
    set sock [socket -server dummy 0]
} -constraints [list socket supported_$af] -body {
    fileevent $sock writable dummy
} -cleanup {
    close $sock
} -returnCodes 1 -result "channel is not writable"

test socket_$af-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 [list socket supported_$af stdio] -body {
    set s [socket $localhost $listen]
    set p [fconfigure $s -peername]
    close $s
    lappend l [string compare [lindex $p 0] $localhost]
    lappend l [string compare [lindex $p 2] $listen]
    lappend l [llength $p]
} -cleanup {
    close $f
} -result {0 0 3}
test socket_$af-7.2 {testing socket specific options} -setup {
    file delete $path(script)
    set f [open $path(script) w]
    puts $f [list set ::tcl::unsupported::socketAF $::tcl::unsupported::socketAF]
    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 [list socket supported_$af stdio] -body {
    set s [socket $localhost $listen]
    set p [fconfigure $s -sockname]
    close $s
    list [llength $p] \
	    [regexp {^(127\.0\.0\.1|0\.0\.0\.0|::1)$} [lindex $p 0]] \
	    [expr {[lindex $p 2] == $listen}]
} -cleanup {
    close $f
} -result {3 1 0}
test socket_$af-7.3 {testing socket specific options} -constraints [list socket supported_$af] -body {
    set s [socket -server accept -myaddr $localhost 0]
    set l [fconfigure $s]
    close $s
    update
    llength $l
} -result 20
test socket_$af-7.4 {testing socket specific options} -constraints [list socket supported_$af] -setup {
    set timer [after 10000 "set x timed_out"]
    set l ""
} -body {
    set s [socket -server accept -myaddr $localhost 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 $localhost $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_$af-7.5 {testing socket specific options} -setup {
    set timer [after 10000 "set x timed_out"]
    set l ""
} -constraints [list socket supported_$af unixOrWin] -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 $localhost $listen]
    vwait x
    lappend l [lindex $x 0] [expr {[lindex $x 2] == $listen}] [llength $x]
} -cleanup {
    after cancel $timer
    close $s
    close $s1
} -result [list $localhost 1 3]
test socket_$af-7.6 {testing socket specific options - bug e589d9bdab} -setup {
    set timer [after 10000 "set x timed_out"]
    set l ""
} -constraints [list socket supported_$af unixOrWin] -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 $localhost $listen]
    vwait x
    lsort [dict keys [fconfigure $s1]]
} -cleanup {
    after cancel $timer
    close $s
    close $s1
} -result {-blocking -buffering -buffersize -encoding -eofchar -keepalive -nodelay -peername -profile -sockname -translation}

test socket_$af-8.1 {testing -async flag on sockets} -constraints [list socket supported_$af] -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 $localhost 0]
    proc accept {s a p} {
	global x
	puts $s bye
	close $s
	set x done
    }
    set s1 [socket -async $localhost [lindex [fconfigure $s -sockname] 2]]
    vwait x
    gets $s1
} -cleanup {
    close $s
    close $s1
} -result bye

test socket_$af-9.1 {testing spurious events} -constraints [list socket supported_$af] -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 $localhost 0]
    set c [socket $localhost [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_$af-9.2 {testing async write, fileevents, flush on close} -constraints [list socket supported_$af] -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 $localhost 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 idle respond $s
    }
    proc respond {s} {
	global firstblock
	puts -nonewline $s $firstblock
	after idle writedata $s
    }
    proc writedata {s} {
	global secondblock
	puts -nonewline $s $secondblock
	close $s
    }
} -body {
    set s [socket $localhost [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_$af-9.3 {testing EOF stickyness} -constraints [list socket supported_$af] -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 $localhost 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 $localhost [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_$af-10.1 {testing socket accept callback error handling} \
    -constraints [list socket supported_$af] -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 $localhost 0]
    proc accept {s a p} {close $s; error}
    set c [socket $localhost [lindex [fconfigure $s -sockname] 2]]
    vwait goterror
    close $s
    close $c
    return $goterror
} -cleanup {
    interp bgerror {} $handler
} -result 1

test socket_$af-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 [list socket supported_$af doTestsWithRemoteServer] -body {
    set s [socket $remoteServerIP $port]
    gets $s
} -cleanup {
    close $s
    sendCommand {close $server}
} -result done
test socket_$af-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 [list socket supported_$af 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_$af-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 [list socket supported_$af doTestsWithRemoteServer] -result ok
test socket_$af-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 [list socket supported_$af 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_$af-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 [list socket supported_$af 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_$af-11.6 {socket conflict} -setup {
    set s1 [socket -server accept -myaddr $localhost 0]
} -constraints [list socket supported_$af doTestsWithRemoteServer] -body {
    set s2 [socket -server accept -myaddr $localhost [getPort $s1]]
    list [getPort $s2] [close $s2]
} -cleanup {
    close $s1
} -returnCodes error -result {couldn't open socket: address already in use}
test socket_$af-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 [list socket supported_$af 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_$af-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 [list socket supported_$af 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_$af-11.9 {accept callback error} -constraints [list socket supported_$af 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_$af-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 [list socket supported_$af 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_$af-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 idle 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 [list socket supported_$af 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_$af-11.12 {testing EOF stickyness} -constraints [list socket supported_$af doTestsWithRemoteServer notWine] -setup {
    set counter 0
    set done 0
    set port [sendCommand {
	set server [socket -server accept 0]
	proc accept {s a p} {
	    after idle 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_$af-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 idle respond $s
	}
	proc respond {s} {
	    global firstblock
	    puts -nonewline $s $firstblock
	    after idle writedata $s
	}
	proc writedata {s} {
	    global secondblock
	    puts -nonewline $s $secondblock
	    close $s
	}
	getPort $l
    }]
    set timer [after 10000 "set done timed_out"]
} -constraints [list socket supported_$af 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_$af-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 {
	fileevent stdin readable exit
	after 10000 exit
	vwait forever
    }
    close $f
    # Script2 creates the server socket, launches script1, 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 [list set localhost $localhost]
    puts $f {
	set f [socket -server accept -myaddr $localhost 0]
	proc accept { file addr port } {
	    close $file
	}
	exec $tcltest $delay &
	puts [lindex [fconfigure $f -sockname] 2]
	close $f
	exit
    }
    close $f
} -constraints [list socket supported_$af stdio exec] -body {
    # Launch script2 and wait 5 seconds
    ### exec [interpreter] script2 &
    set p [open "|[list [interpreter] $path(script2)]" r]
    # If we can still connect to the server, the socket got inherited.
    if {[catch {close [socket $localhost $listen]}]} {
	return {server socket was not inherited}
    } else {
	return {server socket was inherited}
    }
} -cleanup {
    catch {close $p}
} -result {server socket was not inherited}
test socket_$af-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 {
	fileevent stdin readable exit
	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 [list set localhost $localhost]
    puts $f {
	gets stdin port
	set f [socket $localhost $port]
	exec $tcltest $delay &
	puts $f testing
	flush $f
	exit
    }
    close $f
    # If the socket doesn't hit end-of-file in 10 seconds, the script1 process
    # must have inherited the client.
    set timeout 0
    set after [after 10000 {set x "client socket was inherited"}]
} -constraints [list socket supported_$af stdio exec] -body {
    # Create the server socket
    set server [socket -server accept -myaddr $localhost 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
	set ::f $file
    }
    proc getdata { file } {
	# Read handler on the accepted socket.
	global x
	set status [catch {read $file} data]
	if {$status != 0} {
	    set x "read failed, error was $data"
	} elseif {$data ne ""} {
	} elseif {[fblocked $file]} {
	} elseif {[eof $file]} {
	    set x "client socket was not inherited"
	} else {
	    set x "impossible case"
	}
    }
    # 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 {
    fconfigure $f -blocking 1
    close $f
    after cancel $after
    close $p
} -result {client socket was not inherited}
test socket_$af-12.3 {testing inheritance of accepted sockets} -setup {
    file delete $path(script1)
    file delete $path(script2)
    set f [open $path(script1) w]
    puts $f {
	fileevent stdin readable exit
	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 [list set localhost $localhost]
    puts $f {
	set server [socket -server accept -myaddr $localhost 0]
	proc accept { file host port } {
	    global tcltest delay
	    puts $file {test data on socket}
	    exec $tcltest $delay &
	    after idle exit
	}
	puts stdout [lindex [fconfigure $server -sockname] 2]
	vwait forever
    }
    close $f
} -constraints [list socket supported_$af 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
    set f [socket $localhost $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
    set after [after 5000 [list set x "accepted socket was inherited"]]
    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"
	} elseif {[string compare {} $data]} {
	} elseif {[fblocked $file]} {
	} elseif {[eof $file]} {
	    set x "accepted socket was not inherited"
	} else {
	    set x "impossible case"
	}
	return
    }
    vwait x
    set x
} -cleanup {
    fconfigure $f -blocking 1
    close $f
    after cancel $after
    close $p
} -result {accepted socket was not inherited}

test socket_$af-13.1 {Testing use of shared socket between two threads} -body {
    # create a thread
    set serverthread [thread::create -preserved [string map [list @localhost@ $localhost] {
	set f [socket -server accept -myaddr @localhost@ 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
    }]]
    set port [thread::send $serverthread {set listen}]
    set s [socket $localhost $port]
    fconfigure $s -buffering line
    catch {
	puts $s "hello"
	gets $s result
    }
    close $s
    thread::release $serverthread
    append result " " [llength [thread::names]]
} -result {hello 1} -constraints [list socket supported_$af thread]

proc transf_test {{testmode transfer} {maxIter 1000} {maxTime 10000}} {
  try {
    set ::count 0
    set ::testmode $testmode
    set port 0
    set srvsock {}
    # if binding on port 0 is not possible (system related, blocked on ISPs etc):
    if {[catch {close [socket -async $::localhost $port]}]} {
      # simplest server on random port (immediately closing a connect):
      set port [randport]
      set srvsock [socket -server {apply {{ch args} {close $ch}}} -myaddr $::localhost $port]
      # socket on windows has some issues yet (e. g. bug [b6d0d8cc2c]), so we simply decrease iteration count (to 1/4):
      if {$::tcl_platform(platform) eq "windows" && $maxIter > 50} {
	set ::count [expr {$maxIter / 4 * 3 - 1}]; # bypass 3/4 iterations
      }
    }
    tcltest::DebugPuts 2 "== test \[$::localhost\]:$port $testmode =="
    set ::parent [thread::id]
    # helper thread creating async connection and initiating transfer (detach) to parent:
    set ::helper [thread::create]
    thread::send -async $::helper [list \
      lassign [list $::parent $::localhost $port $testmode] \
		     ::parent ::localhost ::port ::testmode
    ]
    thread::send -async $::helper {
      set ::helper [thread::id]
      proc iteration {args} {
	set fd [socket -async $::localhost $::port]
	if {"helper-writable" in $::testmode} {;# to test both sides during connect
	  fileevent $fd writable [list apply {{fd} {
	    if {[thread::id] ne $::helper} {
	      thread::send -async $::parent {set ::count "ERROR: invalid thread, $::helper is expecting"}
	      close $fd
	      return
	    }
	  }} $fd]
	};#
	thread::detach $fd
	thread::send -async $::parent [list transf_parent $fd {*}$args]
      }
      iteration first
    }
    # parent proc committing transfer attempt (attach) and checking acquire was successful:
    proc transf_parent {fd args} {
      tcltest::DebugPuts 2 "** trma / $::count ** $args **"
      thread::attach $fd
      if {"parent-close" in $::testmode} {;# to test close during connect
	set ::count $::count
	close $fd
	return
      };#
      fileevent $fd writable [list apply {{fd} {
	if {[thread::id] ne $::parent} {
	  thread::send -async $::parent {set ::count "ERROR: invalid thread, $::parent is expecting"}
	  close $fd
	  return
	}
	set ::count $::count
	close $fd
      }} $fd]
    }
    # repeat maxIter times (up to maxTime ms as timeout):
    set tout [after $maxTime {set ::count "TIMEOUT"}]
    while 1 {
      vwait ::count
      if {![string is integer $::count]} {
	# if timeout just skip (test was successful until now):
	if {$::count eq "TIMEOUT"} {::tcltest::Skip "timing issue"}
	break
      }
      if {[incr ::count] >= $maxIter} break
      tcltest::DebugPuts 2 "** iter / $::count **"
      thread::send -async $::helper [list iteration nr $::count]
    }
    update
    set ::count
  } finally {
    catch {after cancel $tout}
    if {$srvsock ne {}} {close $srvsock}
    if {[info exists ::helper]} {thread::release -wait $::helper}
    tcltest::DebugPuts 2 "== stop / $::count =="
    unset -nocomplain ::count ::testmode ::parent ::helper
  }
}
test socket_$af-13.2.tr1 {Testing socket transfer between threads during async connect} -body {
    transf_test {transfer} 1000
} -result 1000 -constraints [list socket supported_$af thread]
test socket_$af-13.2.tr2 {Testing socket transfer between threads during async connect} -body {
    transf_test {transfer helper-writable} 100
} -result 100 -constraints [list socket supported_$af thread]
test socket_$af-13.2.cl1 {Testing socket transfer between threads during async connect} -body {
    transf_test {parent-close} 100
} -result 100 -constraints [list socket supported_$af thread]
test socket_$af-13.2.cl2 {Testing socket transfer between threads during async connect} -body {
    transf_test {parent-close helper-writable} 100
} -result 100 -constraints [list socket supported_$af thread]
catch {rename transf_parent {}}
rename transf_test {}

# ----------------------------------------------------------------------

removeFile script1
removeFile script2

# cleanup
if {$remoteProcChan ne ""} {
    catch {sendCommand exit}
}
catch {close $commandSocket}
catch {close $remoteProcChan}
}
unset ::tcl::unsupported::socketAF
test socket-14.0.0 {[socket -async] when server only listens on IPv4} \
    -constraints {socket supported_inet localhost_v4} \
    -setup {
	proc accept {s a p} {
	    global x
	    puts $s bye
	    close $s
	    set x ok
	}
	set server [socket -server accept -myaddr 127.0.0.1 0]
	set port [lindex [fconfigure $server -sockname] 2]
    } -body {
	set client [socket -async localhost $port]
	set after [after $latency {set x [fconfigure $client -error]}]
	vwait x
	set x
    } -cleanup {
	after cancel $after
	close $server
	close $client
	unset x
    } -result ok
test socket-14.0.1 {[socket -async] when server only listens on IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	proc accept {s a p} {
	    global x
	    puts $s bye
	    close $s
	    set x ok
	}
	set server [socket -server accept -myaddr ::1 0]
	set port [lindex [fconfigure $server -sockname] 2]
    } -body {
	set client [socket -async localhost $port]
	set after [after $latency {set x [fconfigure $client -error]}]
	vwait x
	set x
    } -cleanup {
	after cancel $after
	close $server
	close $client
	unset x
    } -result ok
test socket-14.1 {[socket -async] fileevent while still connecting} \
    -constraints {socket} \
    -setup {
	proc accept {s a p} {
	    global x
	    puts $s bye
	    close $s
	    lappend x ok
	}
	set server [socket -server accept -myaddr localhost 0]
	set port [lindex [fconfigure $server -sockname] 2]
	set x ""
    } -body {
	set client [socket -async localhost $port]
	fileevent $client writable {
	    lappend x [fconfigure $client -error]
	    fileevent $client writable {}
	}
	set after [after $latency {lappend x timeout}]
	while {[llength $x] < 2 && "timeout" ni $x} {
	    vwait x
	}
	lsort $x; # we only want to see both events, the order doesn't matter
    } -cleanup {
	after cancel $after
	close $server
	close $client
	unset x
    } -result {{} ok}
test socket-14.2 {[socket -async] fileevent connection refused} \
    -constraints {socket} \
    -body {
	set client [socket -async localhost [randport]]
	fileevent $client writable {set x ok}
	set after [after $latency {set x timeout}]
	vwait x
	after cancel $after
	lappend x [fconfigure $client -error]
    } -cleanup {
	after cancel $after
	close $client
	unset x after client
    } -result {ok {connection refused}}
test socket-14.3 {[socket -async] when server only listens on IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	proc accept {s a p} {
	    global x
	    puts $s bye
	    close $s
	    set x ok
	}
	set server [socket -server accept -myaddr ::1 0]
	set port [lindex [fconfigure $server -sockname] 2]
    } -body {
	set client [socket -async localhost $port]
	set after [after $latency {set x [fconfigure $client -error]}]
	vwait x
	set x
    } -cleanup {
	after cancel $after
	close $server
	close $client
	unset x
    } -result ok
test socket-14.4 {[socket -async] and both, readdable and writable fileevents} \
    -constraints {socket} \
    -setup {
	proc accept {s a p} {
	    puts $s bye
	    close $s
	}
	set server [socket -server accept -myaddr localhost 0]
	set port [lindex [fconfigure $server -sockname] 2]
	set x ""
    } -body {
	set client [socket -async localhost $port]
	fileevent $client writable {
	    lappend x [fconfigure $client -error]
	    fileevent $client writable {}
	}
	fileevent $client readable {lappend x [gets $client]}
	set after [after $latency {lappend x timeout}]
	while {[llength $x] < 2 && "timeout" ni $x} {
	    vwait x
	}
	lsort $x
    } -cleanup {
	after cancel $after
	close $client
	close $server
	unset x
    } -result {{} bye}
# FIXME: we should also have an IPv6 counterpart of this
test socket-14.5 {[socket -async] which fails before any connect() can be made} \
    -constraints {socket supported_inet notWine} \
    -body {
	# address from rfc5737
	socket -async -myaddr 192.0.2.42 127.0.0.1 [randport]
    } \
    -returnCodes 1 \
    -result {couldn't open socket: cannot assign requested address}
test socket-14.6.0 {[socket -async] with no event loop and server listening on IPv4} \
    -constraints {socket supported_inet localhost_v4} \
    -setup {
	proc accept {s a p} {
	    global x
	    puts $s bye
	    close $s
	    set x ok
	}
	set server [socket -server accept -myaddr 127.0.0.1 0]
	set port [lindex [fconfigure $server -sockname] 2]
	set x ""
    } \
    -body {
	set client [socket -async localhost $port]
	for {set i 0} {$i < 50} {incr i } {
	    update
	    if {$x ne ""} {
		lappend x [gets $client]
		break
	    }
	    after 100
	}
	set x
    } \
    -cleanup {
	close $server
	close $client
	unset x
    } \
    -result {ok bye}
test socket-14.6.1 {[socket -async] with no event loop and server listening on IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	proc accept {s a p} {
	    global x
	    puts $s bye
	    close $s
	    set x ok
	}
	set server [socket -server accept -myaddr ::1 0]
	set port [lindex [fconfigure $server -sockname] 2]
	set x ""
    } \
    -body {
	set client [socket -async localhost $port]
	for {set i 0} {$i < 50} {incr i } {
	    update
	    if {$x ne ""} {
		lappend x [gets $client]
		break
	    }
	    after 100
	}
	set x
    } \
    -cleanup {
	close $server
	close $client
	unset x
    } \
    -result {ok bye}
test socket-14.7.0 {pending [socket -async] and blocking [gets], server is IPv4} \
    -constraints {socket supported_inet localhost_v4} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr 127.0.0.1 0]
	    proc accept {s h p} {puts $s ok; close $s; set ::x 1}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	list [fconfigure $sock -error] [gets $sock] [fconfigure $sock -error]
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {{} ok {}}
test socket-14.7.1 {pending [socket -async] and blocking [gets], server is IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr ::1 0]
	    proc accept {s h p} {puts $s ok; close $s; set ::x 1}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	list [fconfigure $sock -error] [gets $sock] [fconfigure $sock -error]
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {{} ok {}}
test socket-14.7.2 {pending [socket -async] and blocking [gets], no listener} \
    -constraints {socket} \
    -body {
	set sock [socket -async localhost [randport]]
	catch {gets $sock} x
	list $x [fconfigure $sock -error] [fconfigure $sock -error]
    } -cleanup {
	close $sock
    } -match glob -result {{error reading "sock*": transport endpoint is not connected} {connection refused} {}}
test socket-14.8.0 {pending [socket -async] and nonblocking [gets], server is IPv4} \
    -constraints {socket supported_inet localhost_v4} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr 127.0.0.1 0]
	    proc accept {s h p} {puts $s ok; close $s; set ::x 1}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	fconfigure $sock -blocking 0
	for {set i 0} {$i < 50} {incr i } {
	    if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
	    after 200
	}
	set x
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {ok}
test socket-14.8.1 {pending [socket -async] and nonblocking [gets], server is IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr ::1 0]
	    proc accept {s h p} {puts $s ok; close $s; set ::x 1}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	fconfigure $sock -blocking 0
	for {set i 0} {$i < 50} {incr i } {
	    if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
	    after 200
	}
	set x
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {ok}
test socket-14.8.2 {pending [socket -async] and nonblocking [gets], no listener} \
    -constraints {socket} \
    -body {
	set sock [socket -async localhost [randport]]
	fconfigure $sock -blocking 0
	for {set i 0} {$i < 50} {incr i } {
	    if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
	    after 200
	}
	list $x [fconfigure $sock -error] [fconfigure $sock -error]
    } -cleanup {
	close $sock
    } -match glob -result {{error reading "sock*": transport endpoint is not connected} {connection refused} {}}
test socket-14.9.0 {pending [socket -async] and blocking [puts], server is IPv4} \
    -constraints {socket supported_inet localhost_v4} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr 127.0.0.1 0]
	    proc accept {s h p} {set ::x $s}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	    puts [gets $x]
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	puts $sock ok
	flush $sock
	list [fconfigure $sock -error] [gets $fd]
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {{} ok}
test socket-14.9.1 {pending [socket -async] and blocking [puts], server is IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr ::1 0]
	    proc accept {s h p} {set ::x $s}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	    puts [gets $x]
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	puts $sock ok
	flush $sock
	list [fconfigure $sock -error] [gets $fd]
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {{} ok}
test socket-14.10.0 {pending [socket -async] and nonblocking [puts], server is IPv4} \
    -constraints {socket supported_inet localhost_v4} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr 127.0.0.1 0]
	    proc accept {s h p} {set ::x $s}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	    puts [gets $x]
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	fconfigure $sock -blocking 0
	puts $sock ok
	flush $sock
	fileevent $fd readable {set x 1}
	vwait x
	list [fconfigure $sock -error] [gets $fd]
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {{} ok}
test socket-14.10.1 {pending [socket -async] and nonblocking [puts], server is IPv6} \
    -constraints {socket supported_inet6 localhost_v6} \
    -setup {
	makeFile {
	    fileevent stdin readable exit
	    set server [socket -server accept -myaddr ::1 0]
	    proc accept {s h p} {set ::x $s}
	    puts [lindex [fconfigure $server -sockname] 2]
	    flush stdout
	    vwait x
	    puts [gets $x]
	} script
	set fd [open |[list [interpreter] script] RDWR]
	set port [gets $fd]
    } -body {
	set sock [socket -async localhost $port]
	fconfigure $sock -blocking 0
	puts $sock ok
	flush $sock
	fileevent $fd readable {set x 1}
	vwait x
	list [fconfigure $sock -error] [gets $fd]
    } -cleanup {
	close $fd
	close $sock
	removeFile script
    } -result {{} ok}
test socket-14.11.0 {pending [socket -async] and nonblocking [puts], no listener, no flush} \
    -constraints {socket notWinCI} \
    -body {
	set sock [socket -async localhost [randport]]
	fconfigure $sock -blocking 0
	puts $sock ok
	fileevent $sock writable {set x 1}
	vwait x
	close $sock
    } -cleanup {
	catch {close $sock}
	unset x
    } -result {transport endpoint is not connected} -returnCodes 1
test socket-14.11.1 {pending [socket -async] and nonblocking [puts], no listener, flush} \
    -constraints {socket testsocket_testflags} \
    -body {
	set sock [socket -async localhost [randport]]
	# Set the socket in async test mode.
	# The async connect will not be continued on the following fconfigure
	# and puts/flush. Thus, the connect will fail after them.
	testsocket testflags $sock 1
	fconfigure $sock -blocking 0
	puts $sock ok
	flush $sock
	testsocket testflags $sock 0
	fileevent $sock writable {set x 1}
	vwait x
	close $sock
    } -cleanup {
	catch {close $sock}
	catch {unset x}
    } -result {transport endpoint is not connected} -returnCodes 1
test socket-14.12 {[socket -async] background progress triggered by [fconfigure -error]} \
    -constraints {socket} \
    -body {
	set s [socket -async localhost [randport]]
	for {set i 0} {$i < 50} {incr i} {
	    set x [fconfigure $s -error]
	    if {$x ne ""} break
	    after 200
	}
	set x
    } -cleanup {
	close $s
	unset x s
    } -result {connection refused}

test socket-14.13 {testing writable event when quick failure} \
    -constraints {socket win supported_inet notWine} \
    -body {
    # Test for bug 336441ed59 where a quick background fail was ignored

    # Test only for windows as socket -async 255.255.255.255 fails
    # directly on Unix

    # The following connect should fail very quickly
    set a1 [after 2000 {set x timeout}]
    set s [socket -async 255.255.255.255 43434]
    fileevent $s writable {set x writable}
    vwait x
    set x
} -cleanup {
    catch {close $s}
    after cancel $a1
} -result writable

test socket-14.14 {testing fileevent readable on failed async socket connect} \
    -constraints {socket} -body {
    # Test for bug 581937ab1e

    set a1 [after 5000 {set x timeout}]
    # This connect should fail
    set s [socket -async localhost [randport]]
    fileevent $s readable {set x readable}
    vwait x
    set x
} -cleanup {
    catch {close $s}
    after cancel $a1
} -result readable

test socket-14.15 {blocking read on async socket should not trigger event handlers} \
    -constraints socket -body {
	set s [socket -async localhost [randport]]
	set x ok
	fileevent $s writable {set x fail}
	catch {read $s}
	close $s
	set x
    } -result ok

# v4 and v6 is required to prevent that the async connect does not terminate
# before the fconfigure command. There is always an additional ip to try.
test socket-14.16 {empty -peername while [socket -async] connecting} \
    -constraints {socket localhost_v4 localhost_v6} \
    -body {
	set client [socket -async localhost [randport]]
	fconfigure $client -peername
    } -cleanup {
	catch {close $client}
    } -result {}

# v4 and v6 is required to prevent that the async connect does not terminate
# before the fconfigure command. There is always an additional ip to try.
test socket-14.17 {empty -sockname while [socket -async] connecting} \
    -constraints {socket localhost_v4 localhost_v6} \
    -body {
	set client [socket -async localhost [randport]]
	fconfigure $client -sockname
    } -cleanup {
	catch {close $client}
    } -result {}

# test for bug c6ed4acfd8: running async socket connect with other connect
# established will block tcl as it goes in an infinite loop in vwait
test socket-14.18 {bug c6ed4acfd8: running async socket connect made other connect block} \
    -constraints {socket} \
    -body {
	proc accept {channel address port} {}
	set port [randport]
	set ssock [socket -server accept $port]
	set csock1 [socket -async localhost [randport]]
	set csock2 [socket localhost $port]
	after 1000 {set done ok}
	vwait done
} -cleanup {
	catch {close $ssock}
	catch {close $csock1}
	catch {close $csock2}
    } -result {}

test socket-14.19 {tip 456 -- introduce the -reuseport option} \
    -constraints {socket notWine} \
    -body {
	proc accept {channel address port} {}
	set port [randport]
	set ssock1 [socket -server accept -reuseport yes $port]
	set ssock2 [socket -server accept -reuseport yes $port]
	return ok
} -cleanup {
    catch {close $ssock1}
    catch {close $ssock2}
    } -result ok

set num 0

set x {localhost {socket} 127.0.0.1 {supported_inet} ::1 {supported_inet6}}
set resultok {-result "sock*" -match glob}
set resulterr {
    -result {couldn't open socket: connection refused}
    -returnCodes 1
}
foreach {servip sc} $x {
    foreach {cliip cc} $x {
	set constraints socket
	lappend constraints $sc $cc
	set result $resulterr
	switch -- [lsort -unique [list $servip $cliip]] {
	    localhost - 127.0.0.1 - ::1 {
		set result $resultok
	    }
	    {127.0.0.1 localhost} {
		if {[testConstraint localhost_v4]} {
		    set result $resultok
		}
	    }
	    {::1 localhost} {
		if {[testConstraint localhost_v6]} {
		    set result $resultok
		}
	    }
	}
	test socket-15.1.$num "Connect to $servip from $cliip" \
	    -constraints $constraints -setup {
		set server [socket -server accept -myaddr $servip 0]
		proc accept {s h p} { close $s }
		set port [lindex [fconfigure $server -sockname] 2]
	    } -body {
		set s [socket $cliip $port]
	    } -cleanup {
		close $server
		catch {close $s}
	    } {*}$result
	incr num
    }
}

test socket-bug-31fc36fe47 "Crash listening in multiple threads" \
    -constraints thread -body {
	close [socket -server xxx 0]
	set tid [thread::create]
	thread::send $tid {close [socket -server accept 0]}
	thread::release $tid
    } -result 0

::tcltest::cleanupTests
flush stdout
return

# Local Variables:
# mode: tcl
# fill-column: 78
# End:
