# websocket.test - Copyright (c) 2022 Keith Nash # # The tests use the WebSocket Echo Server hosted at ws.ifelse.io. # ------------------------------------------------------------------------------ source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] testsNeedTcl 8.6 testsNeedTcltest 2 testing { useLocal websocket.tcl websocket } # The commands ws*Handler* process responses from the server. proc wsResetHandler {} { set ::wsMsgCounter 0 set ::wsOutput {} return } proc wsExpectHandler {lines} { set ::wsMsgMax $lines return } proc wsOutputHandler {args} { append ::wsOutput $args \n # Stop sleeping if we've had $wsMsgMax messages. incr ::wsMsgCounter if {$::wsMsgCounter == $::wsMsgMax} { wsStopSleep 1 } return } # - The commands ws*Sleep allow time for the server to responses to arrive and # be processed by the output handler. # - The default value of delay is a worst case for a slow server: the test will # time out if this value is reached. The output handler counts the messages # and interrupts the sleep when the expected number of messages has arrived. # - In the body of test, a second call to wsSleep gives the server the # opportunity to send excess messages. proc wsSleep {{delay 10000}} { if {$::wsWAITER != 0} { # The wake-up call has arrived already. return } set ev [after $delay set ::wsWAITER -1] vwait ::wsWAITER after cancel $ev return } proc wsResetSleep {} { set ::wsWAITER 0 return } proc wsStopSleep {val} { set ::wsWAITER $val return } set wsNum 0 # Set the test URL. # The value of expectNum, and some of the result strings, depend on the # server configuration and therefore on the URL. set wsUrl ws://ws.ifelse.io set expectNum 2 test example-1.1 "Open a websocket" -constraints {} -setup { wsResetSleep wsResetHandler } -body { wsExpectHandler $expectNum set ws1 [::websocket::open $wsUrl wsOutputHandler] wsSleep wsResetSleep wsSleep 1000 string map [list $ws1 SOCKET] $wsOutput } -cleanup { ::websocket::close $ws1 wsSleep wsResetSleep wsResetHandler } -match glob -result {SOCKET connect {} SOCKET text {Request served by *} } test example-1.2 "Close a websocket" -constraints {} -setup { wsResetHandler wsResetSleep wsExpectHandler $expectNum set ws1 [::websocket::open $wsUrl wsOutputHandler] wsSleep wsResetHandler wsResetSleep } -body { ::websocket::close $ws1 wsSleep wsResetSleep wsSleep 1000 string map [list $ws1 SOCKET] $wsOutput } -cleanup { wsResetSleep wsResetHandler } -match exact -result {SOCKET close {1000 {Normal closure}} SOCKET disconnect {Disconnected from remote end} } test example-1.3 "Open second websocket - require different socket" -constraints {} -setup { wsResetSleep wsResetHandler wsExpectHandler $expectNum set ws1 [::websocket::open $wsUrl wsOutputHandler] wsSleep wsResetSleep wsResetHandler } -body { wsExpectHandler $expectNum set ws2 [::websocket::open $wsUrl wsOutputHandler] if {$ws1 eq $ws2} { set res "Both sockets are $ws1" } else { set res "Sockets differ: $ws1 and $ws2" } } -cleanup { catch {::websocket::close $ws1} catch {::websocket::close $ws2} wsResetSleep wsResetHandler } -match glob -result {Sockets differ: *} test example-1.4 "Open second websocket - usual server return" -constraints {} -setup { wsResetSleep wsResetHandler wsExpectHandler $expectNum set ws1 [::websocket::open $wsUrl wsOutputHandler] wsSleep wsResetSleep wsResetHandler } -body { wsExpectHandler $expectNum set ws2 [::websocket::open $wsUrl wsOutputHandler] if {$ws1 eq $ws2} { return -code error "Both sockets are $ws1" } wsSleep wsResetSleep wsSleep 1000 string map [list $ws2 SOCKET] $wsOutput } -cleanup { catch {::websocket::close $ws1} catch {::websocket::close $ws2} wsResetSleep wsResetHandler } -match glob -result {SOCKET connect {} SOCKET text {Request served by *} } test example-2.1 "Send a message" -constraints {} -setup { wsResetHandler wsResetSleep wsExpectHandler $expectNum set ws1 [::websocket::open $wsUrl wsOutputHandler] wsSleep wsResetHandler wsResetSleep } -body { wsExpectHandler 1 set ::newMessage "Hello this is message <[incr wsNum]>" ::websocket::send $ws1 text $::newMessage wsSleep wsResetSleep wsSleep 1000 string map [list $ws1 SOCKET <${wsNum}> NUMBER] $wsOutput } -cleanup { wsExpectHandler $expectNum ::websocket::close $ws1 wsSleep wsResetSleep wsResetHandler } -match exact -result {SOCKET text {Hello this is message NUMBER} } testsuiteCleanup unset -nocomplain wsNum wsMsgCounter wsOutput wsMsgMax wsWAITER wsUrl expectNum ws1 ws2 newMessage foreach pr { wsResetHandler wsExpectHandler wsOutputHandler wsSleep wsResetSleep wsStopSleep } { rename $pr {} } # Local variables: # mode: tcl # indent-tabs-mode: nil # End: