# 
# Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_lib "dejagnu.exp"

# If tracing has been enabled at the top level, then turn it on here
# too.
if $tracelevel {
    strace $tracelevel
}

# After these many seconds of execution the test script is aborted with a failure.
# This is to handle deadlocks. We don't reset the timeout when a match is
# found to avoid hanging in case of a testcase sending matches in an infinite loops.
# (not unlikely as it seems, think about flash movies...)
set timeout 10
set file all
set params ""

set cport 6080;			# the port for the connection to Cygnal
set chost "localhost";		# the host to connect to
set sid 0;			# the server ID

set server_dead "false";	# toggle is Cygnal dies

proc delete_files {} {
    global chost
    global cport

    # Get rid of old temp files created by testing,
    if {[file exists foo]} {
	set junk [glob foo* wget-log* amf0* *.bin* gateway* $chost:$cport]
	if { $junk != "" } {
	    catch "exec rm -fr $junk"
	}
    }
}

#
# Create a bogus test file with predictable data we can use to test file
# transfers.
#
proc create_file { name size } {
    verbose "Trying to create $name" 2
    set fd [ open $name "w" ]
    for { set i 0 } { $i < $size } { incr i } {
	# split out the argument for options that take them
	puts -nonewline $fd "$i "
    }
    flush $fd
#    catch "close -i $fd"
}

#
# Send something to the copy of Cygnal we started
proc netcat { name } {
    global chost
    global cport
    verbose "Trying to netcat $name to Cygnal $chost : $cport)"
    global server_dead 
    spawn -noecho nc $chost $cport
    set nid $spawn_id
    global timeout

    expect {
	eof {
	    verbose "Netcat all done..."
	}
	timeout {
	    verbose "Netcat still running after ${timeout} seconds, killing it (deadlock?)"
	    catch close
	    set server_dead "true"
	}
    }
#verbose "Started NetCat $netcat -q 10"
    
}

#
# Get something from the copy of Cygnal we started
proc wget { name options } {
    global chost
    global cport
    global sid
    global server_dead 
    global timeout
    set result false

    # --header="Content-Type: application/x-amf"
    verbose "Trying to wget from Cygnal -p -v --tries=3 --server-response $options http://$chost:$cport/$name" 2
    spawn -noecho wget -p -v --tries=3 --server-response $options "http://$chost:$cport/$name"
    set wid $spawn_id
    expect {
	-i $wid "*Saving to: *" {
	    verbose "Wget all done..." 2
	    set result true
	}
	-i $wid "* saved " {
	    verbose "Wget all done..." 2
	    set result true
	}
	-i $wid "Giving up" {
	    verbose "Giving up on file transfer via GET"
	}
# 	-i $wid "Retrying." {
# 	    verbose "Retrying file"
# 	    exp_continue
# 	}
	-i $wid "Connection close" {
	    verbose "Connection closed from other end"
	}
	-i $wid "Content-Type: application/x-amf" {
	    exp_continue
	}
	-i $wid "HTTP/1.1 200 OK" {
	    exp_continue
	}
	-i $wid "connected." {
	    exp_continue
	}
	-i $wid "has sprung into existence" {
	    verbose "File transferred but no size"
	    set result false
	}
	-i $wid "Connection closed at" {
	}
	-i $wid "*Connection refused" {
	    set server_dead "true"
	    set result false
	    verbose "Wget failed..."
	}
	-i $wid "Content-Type: " {
	    verbose "Got HTTP response header $name" 2
	    set result true
	}
	-i $wid eof {
	    verbose "Wget EOF..."
	    set result true
	}
	-i $wid timeout {
	    verbose "Wget still running after ${timeout} seconds, killing it (deadlock?)"
	    catch close -i $sid	    
	    set result false
	    set server_dead "true"
	}
    }

    catch {exec "kill -INT [exp_pid -i $wid]"}
    catch {close -i $wid}

    return $result
}

#
# Get something from the copy of Cygnal we started
proc wpost { name options } {
    global server_dead 
    global chost
    global cport
    global sid
    global timeout
    set result false
    # --header="Content-Type: application/x-amf"
    verbose "Trying to wget POST to Cygnal -p -v --tries=3 --server-response --header=\"Content-Type: application/x-amf\" $options --post-file=$name http://$chost:$cport/echo/gateway" 2
#    set where "[pwd]/${name}_barfoo"
    spawn  wget -p -v --tries=3 --server-response  --post-file=$name --header "Content-Type: application/x-amf" "http://$chost:$cport/echo/gateway"
    set wid $spawn_id
    expect {
	-i $wid "*Saving to: *" {
	    verbose "Wget all done..." 2
	    set result true
	}
	-i $wid "* saved " {
	    verbose "Wget all done..." 2
	    set result true
	}
	-i $wid "Giving up" {
	    verbose "Giving up on file transfer via POST"
	    set result false
	    set server_dead true
	}
# 	-i $wid "Retrying." {
# 	    verbose "Retrying file"
# 	    exp_continue
# 	}
	-i $wid "Connection close" {
	    verbose "Connection closed from other end"
	}
	-i $wid "connected." {
	    exp_continue
	}
	-i $wid "has sprung into existence" {
	    verbose "File transferred but no size"
	    set result false
	}
	-i $wid "Connection closed at" {
	    set server_dead true
	}
	-i $wid "No data received" {
	    set server_dead true
	}
	-i $wid "*Connection refused" {
	    set server_dead true
	    verbose "Wget failed..."
	}
	-i $wid "Content-Type: " {
	    verbose "Got HTTP response header $name" 2
	    set result true
	}
	-i $wid eof {
	    verbose "Wget EOF..."
	    set result true
	}
	-i $wid timeout {
	    verbose "Wget still running after ${timeout} seconds, killing it (deadlock?)"
	    catch close -i $sid
	    set result false
	    set server_dead "true"
	}
    }

    catch {exec "kill -INT [exp_pid -i $wid]"}
    catch {close -i $wid}

    return $result
}

#
# Start the server running. 
#
proc start_cygnal { } {
    global server_dead 
    global sid
    global objdir
    set result false
    
    # Kill any old ones still running
    if {$sid > 0} {
	catch exec "kill -QUIT [exp_pid -i $sid]"
	catch {close -i $sid}
    }

    # find the right cygnal in our build tree to run
    set server [lookfor_file $objdir cygnal]
    set options "-p 6000 -vv -n -t -r [pwd]"
    verbose "Starting Cygnal server \"$server $options\" for testing"
    spawn  $server $options
    set sid $spawn_id

    send "\r\n"
    expect -timeout 1 {
	-i $sid "\r\n\r\n" {
	    verbose "Cygnal is trying to start..."
	}
	-i $sid "*Server bound to service" {
	    verbose "Cygnal \"$server -vv -n\" bound to port"
	    set result true
	}
	-i $sid "*Starting*" {
	    verbose "Started Cygnal \"$server -vv -n\""
	    set result true
	}
	-i $sid "File * transferred" {
	    verbose "File transferred"
	}
	-i $sid "unable to bind to port" {
	    log_error "Another Cygnal is already on this port!"
	    set server_dead "true"
	    catch close -i $sid
	}
	-i $sid "HTTP Handler" {
	    verbose "Started HTTP Handler"
	}
	-i $sid eof {
	    #	    unresolved "${file} died prematurely"
	    #	    catch close
	    verbose "${file} died prematurely"
	}
 	timeout {
 	    fail "Test case ${server} still running after ${timeout} seconds, killing it (deadlock?)"
 	}
	-i $sid timeout {
		fail "Test case ${server} still running after ${timeout} seconds, killing it (deadlock?)"
	    verbose "Cygnal timed out, killing PID $pid"
	    catch exec "kill -INT [exp_pid -i $sid]"
	    catch {close -i $sid}
	}
    }

    set server_dead "false"

    return $result
}

set netcat "netcat"
#set nid [spawn -noecho $netcat]
#verbose "Started NetCat $netcat -q 10"

delete_files

start_cygnal

#
# Test transferring files from the server to the client
#

#
# Test transferring a single file
#

# create test file
create_file "foo" 320

# get the test file
set result [ wget "foo" "" ]
if { $result == true } {
    pass "HTTP GET single file"
} else {
    fail "HTTP GET single file"
}

#
# Testing transferring multiple files in a nice tight loop for stress testing.
#
set files ""
set limit 12
set count 0
for { set i 1 } { $i <= $limit } { incr i } {
    set size [ expr $i * 120]
    create_file "foo$i" $size
    set result [ wget "foo$i" "" ]
    if { $result == true } {
	verbose "foo$i was transferred!" 2
	incr count
	verbose -n "."
    } else {
	unresolved "HTTP GET multiple files"
    }
}
#catch close -i $sid

verbose "."
if { $count == $limit } {
    pass "HTTP GET multiple files"
} else {
    fail "HTTP GET multiple files"
}

# Red5, a java based RTMP server has a nice test case that lets one
# bounce AMF data types off of a server using either RTMPT or RTMP,
# and we collected all of these from net captures of those. While some
# of the unit tests in the top level testsuite/libnet.all use these
# same captures differently, these are the original raw files those
# tests are based on. We need disk based files because wget needs
# those to calculate the size properly for the "Content-Length" header
# field. 
set binsdir "[file dirname [file dirname [file dirname $srcdir]]]/cygnal/testsuite/cygnal.all/bindata"
set requests  [glob -nocomplain $binsdir/*-request.bin]
set responses [glob -nocomplain $binsdir/*-response.bin]

# test with the Red5 echo tests

set limit 10
# Beat the living daylights out of Cygnal, it'll have to handle much worse.
# There are limits to what we can do testing on just one machine, but this
# catches quite a few problems that otherwise were hard to reproduce. There
# are limits to what we can do on one single machine. Ultimately, this needs
# to fire up tests running from multiple hosts in the build farm, which should
# be totally doable in 
#
# Just as a note, I've found while testing that when running Cygnal in
# single threaded mode, it can't keep up with hundreds of simultaneous
# network connections, so many wgets processes all go zombie waiting to
# get disconnected. So for single threaded mode, don't set the limit over
# 10, it's just for testing protocol stuff anyway! Multi threaded testing
# works mode works much better, I've had the limit set much higher.

for { set k 0 } { $k <= $limit } { incr k } {
    foreach i $requests {
	# Restart the server if it got hung or died running the previous test case.
	if { $server_dead == "true" } {
	    start_cygnal
	}
	set pos [string last  "/" $i]
	if { $pos > 0 } {
	    set j [string range $i $pos+1 end]
	}
	#    set options "--header='Content-Type: application/x-amf' --post-file=$i"
	if {$server_dead == "false"} {
	    set result [ wpost $i "" ]
	    if { $result == true } {
		pass "HTTP POST AMF0 $j, sent"
	    } else {
		fail "HTTP POST AMF0 $j, sent"
		set server_dead "true"
	    }
	} else {
	    untested "HTTP POST AMF0 $j, sent"
	    untested "HTTP POST AMF0 $j got no response, not saved to $chost:$cport/gateway"
	    set server_dead "true"
	}
	# If the server is dead, kill the remnants
	if { $server_dead == "true" } {
	    puts "Server PID: [exp_pid -i $sid] appears to be dead"
	    catch {exec "kill -INT [exp_pid -i $sid]"}
	    catch {close -i $sid}
	    catch {exec "kill -INT [exp_pid -i $wid]"}
	    catch {close -i $wid}
	} else {
	    set gotback "[string trimright $i "-request.bin"]-response.bin"
	    puts $gotback
	    if {[file exists $chost:$cport/echo/gateway]} {
		pass "HTTP POST AMF0  got response, saved to $chost:$cport/gateway"
		# 	puts "[file size $chost:$cport/echo/gateway]"
		# set res [exec cmp $var $gotback]
	    } else {
		fail "HTTP POST AMF0 $j got response, saved to $chost:$cport/gateway"
	    }
	}
    }
    # if we don't sleep here and give the poor host machine a rest, it can't handle
    # any more wget processes running at the same time. I was winding up with
    # hundreds of zombies that eat my quad xeon machine with 4 gig of RAM!
#    sleep 1
}

delete_files

#############################################################
# Stop the server, we're done. As the server doesn't die when a connection is
# closed, we have to kill it, and then close the pty handle.
if {$sid > 0} {
    set pid [exp_pid -i $sid]
    verbose "Attempting to shut down Cygnal, PID is: $pid"
    catch {exec kill -INT $pid}
    catch {close -i $sid}
}

# testcases is set by the Makefile in the site.exp data file.
#foreach file $testcases {
#     expect {
# 	-i $sid "*Starting*" {
# 	    verbose "Started Cygnal \"$server -p 4000 -vv -n\""
# 	}
# 	-i $sid "HTTP Handler" {
# 	    verbose "Started HTTP Handler"
# 	}
# 	-i $sid eof {
# 	    #	    unresolved "${file} died prematurely"
# 	    #	    catch close
# 	    #	    return "${file} died prematurely"
# 	}
# 	-i $sid timeout {
# 		fail "Test case ${server} still running after ${timeout} seconds, killing it (deadlock?)"
# #		catch close -i $sid
# #		continue;
# 	}
#     }

#     verbose "Running test $file"

#     # spawn the executable and look for the DejaGnu output messages from the
#     # test case.

#     # this version of the call allows use of 'wait' to check return code
#     # -open [open "|cmd" "r"] doesn't work for that
#     spawn -noecho ./$file 

#     expect {
# 	-re "^\[^\n]*NOTE:\[^\n]*\n" {
# 	    regsub ".*NOTE: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2]
# 	    verbose "${file} $output" 
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*XPASSED:\[^\n]*\n" {
# 	    regsub ".*XPASSED: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2]
# 	    xpass "${file}: $output"
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*PASSED:\[^\n]*\n" {
# 	    regsub ".*PASSED: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2]
# 	    pass "${file}: $output"
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*XFAILED:\[^\n]*\n" {
# 	    regsub ".*XFAILED: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2] 
# 	    xfail "${file}: $output"
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*FAILED:\[^\n]*\n" {
# 	    regsub ".*FAILED: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2] 
# 	    fail "${file}: $output"
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*UNTESTED:\[^\n]*\n" {
# 	    regsub ".*UNTESTED: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2]
# 	    untested "${file}: $output"
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*UNRESOLVED:\[^\n]*\n" {
# 	    regsub ".*UNRESOLVED: " $expect_out(0,string) "" output
# 	    set output [string range $output 0 end-2]
# 	    unresolved "${file}: $output"
# 	    exp_continue -continue_timer
# 	}
# 	-re "^\[^\n]*\n" {
#             # just remove non-matching lines!
#             exp_continue -continue_timer
# 	}
# 	eof {
# 	    #	    unresolved "${file} died prematurely"
# 	    #	    catch close
# 	    #	    return "${file} died prematurely"
# 	}
# 	timeout {
# 		fail "Test case ${file} still running after ${timeout} seconds, killing it (deadlock?)"
# 		catch close
# 		continue;
# 	}
#     }

#     # wait for the process to coplete to
#     # check return code
#     set retcode [wait]
    
#     # debugging
#     #set i 0; foreach j $retcode { print "${file} wait($i) $j"; incr i }
    
#     if { [ llength $retcode ] > 5 } {
# 		fail "${file} died prematurely ([lindex $retcode 6])"
#     }
#     if { [ lindex $retcode 3 ] != 0 } {
# 	fail "${file} exited with non-zero code ([lindex $retcode 3])"
#     }

    
    
#    # force a close of the executable to be safe.
#    catch close
#}


