【发布时间】:2015-04-21 07:00:12
【问题描述】:
好的,我在一家电信公司工作,我为我们的产品开发了一些自动化测试。我正在使用 TCL8.4 和 Expect 5.44.1.15,因为当我被聘用时有人问我。大多数测试包括使用 telnet 与我们产品的 shell 交互。所以我想出了下面的功能:
# ------------------------------------------------------------------------------
# Description: Launch telnet session with specified IP and port and assign
# resulting spawned process ID to processIDRef upon success.
#
# Parameters: ipAddr - in: IPv4 address to connect with.
# port - in: Port number to connect with.
# prompt - in: String indicating a successfull connection.
# processIDRef - out: Resulting spawned process identifier.
#
# Return: Returns no significant value. Throws exception on failure.
# ------------------------------------------------------------------------------
proc ::connections::telnet::Connect {ipAddr port prompt processIDRef} {
# Print function name and parameters
::utils::PrintDebug [info level 0]
# Parameter reference(s)
upvar $processIDRef processID
# Validate parameters
if {![regexp -- {^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}} $ipAddr]} {
::utils::Throw "Unsupported IP addresse format/value: \"$ipAddr\""
} elseif {[string length $port] && !([string is integer $port] && $port >= 0)} {
::utils::Throw "Unsupported port number format/value: \"$port\""
}
# Spawn telnet session and expect prompt.
if {![string length $port]} {
# passing an empty string as port argument to telnet causes an error
spawn telnet $ipAddr
} else {
spawn telnet $ipAddr $port
}
AddActiveSession $spawn_id
Expect $spawn_id $prompt 30
set processID $spawn_id
}
# ------------------------------------------------------------------------------
# Description: Close telnet session associated with processID.
#
# Parameters: processID - in: Spawned process identifier.
#
# Return: Returns no significant value. Throws exception on failure.
# ------------------------------------------------------------------------------
proc ::connections::telnet::Disconnect {processID} {
# Print function name and parameters
::utils::PrintDebug [info level 0]
# Validate parameters
if {![IsActiveSession $processID]} {
::utils::Throw "Unknown/Invalid processID: \"$processID\""
}
if {[::utils::IsProcessAlive $processID]} {
# Open Telnet Prompt
SendCommand $processID "\x1D" "" "telnet> " 10
# Close telnet session
SendCommand $processID "close" {\n} "Connection closed." 10
# Remove from active sessions and close expect connection
RemoveActiveSession $processID
wait -i $processID
close -i $processID
} else {
# Process is dead.
RemoveActiveSession $processID
}
}
# ------------------------------------------------------------------------------
# Description: Send command to process and expect response within alloted time.
# Response can be collected inside a buffer if provided a reference.
#
# Parameters: processID - in: Spawnded process identifier.
# command - in: String to be sent to process.
# terminator - in: Character to append to command, signaling the
# end of the command string for the interpreter.
# prompt - in: Regular Expression indicating end of command
# execution.
# timelimit - in: amount time we expect results before declaring
# failed execution.
# bufferRef - out: Resulting command execution output.
#
# Return: 1 if prompt was found within the timelimit, otherwise
# throws an exception.
# ------------------------------------------------------------------------------
proc ::connections::telnet::SendCommand {processID command terminator prompt timelimit {bufferRef ""}} {
# Print function name and parameters
::utils::PrintDebug [info level 0]
# Parameter reference(s)
if {[string length $bufferRef]} {
upvar $bufferRef buffer
}
set buffer ""
# Set expect parameters
set spawn_id $processID
# Send command and expect results
send -s "$command[subst $terminator]"
Expect $processID $prompt $timelimit buffer
}
# ------------------------------------------------------------------------------
# Description: Expect response within alloted time.
# Response can be collected inside a buffer if provided a reference.
#
# Parameters: processID - in: Spawnded process identifier.
# prompt - in: Regular Expression indicating end of command
# execution.
# timelimit - in: amount time we expect results before declaring
# failed execution.
# bufferRef - out: Resulting command execution output.
#
# Return: Returns no significant value. Throws exception on failure.
# ------------------------------------------------------------------------------
proc ::connections::telnet::Expect {processID prompt timelimit {bufferRef ""}} {
# Print function name and parameters
::utils::PrintDebug [info level 0]
# Parameter reference(s)
if {[string length $bufferRef]} {
upvar $bufferRef buffer
}
set buffer ""
# Validate parameters
if {![IsActiveSession $processID]} {
::utils::Throw "Unknown/Invalid processID: \"$processID\""
} elseif {!([string is integer $timelimit] && $timelimit >= 0)} {
::utils::Throw "Unsupported timeout format/value: \"$timelimit\""
}
# Set expect parameters
set spawn_id $processID
set timeout $timelimit
# expect results
expect {
-re "$prompt" {
# Command Successful
if {[info exists expect_out(buffer)]} {
append buffer $expect_out(buffer)
}
# Print Buffer to log file at least
::utils::PrintDebug $buffer
}
full_buffer {
# expect_out(buffer) is full
if {[string length $bufferRef]} {
append buffer $expect_out(buffer)
}
exp_continue
}
timeout {
# Timeout
if {[info exists expect_out(buffer)]} {
append buffer $expect_out(buffer)
}
# Print Buffer to log file at least
::utils::PrintDebug $buffer
# Throw exception
::utils::Throw "Timed out while waiting for \"$prompt\"."
}
eof {
# Process reached it's end
if {[string length $bufferRef] && [info exists expect_out(buffer)]} {
append buffer $expect_out(buffer)
}
# Print Buffer to log file at least
::utils::PrintDebug $buffer
# Throw exception
::utils::Throw "Reached eof while waiting for \"$prompt\"."
}
}
}
基本上可以使用如下函数:
- ::connections::telnet::Connect
- 使用 telnet 连接到 IP 和端口并等待特定提示。
- ::connections::telnet::Disconnect
- 关闭远程登录。
- ::connections::telnet::SendCommand
- 通过 telnet 发送命令并期望在分配的时间内出现特定提示。
- ::connections::telnet::Expect
- 在分配的时间内期待某个提示而不发送命令。
问题:
有时会出现一个错误,我不知道是什么原因造成的。有时我使用 SendCommand 并且没有输出存储在期望缓冲区中,因此显然该函数超时并引发异常。除了如果我期待第二次,那么我们可以清楚地看到(在我的日志中)缓冲区现在确实包含我之前调用不超时所需的输出。
这是我的一些调试日志,向您展示它的样子:
Feb/18/2015 18:16:17: ::connections::telnet::SendCommand exp5 {~ restart dispGates} {\n} {----.*> } 10 缓冲区
2015 年 2 月 18 日 18:16:17:预期 exp5 {----.*> } 10 缓冲区
Feb/18/2015 18:16:27:这是记录缓冲区内容的地方,不应为空
Feb/18/2015 18:16:27:等待“----.*>”时超时。
...
Feb/18/2015 18:16:28: ::connections::telnet::SendCommand exp5 {~ *} {\n} {连接被外部主机关闭。} 30
Feb/18/2015 18:16:29: Expect exp5 {Connection closed by foreign host.} 30 buffer
这是我在最后一次通话中期待的输出!您可以清楚地看到我在此发送的两个命令。
Feb/18/2015 18:16:30: ~ 重启 dispGates
RST_GATE_INITIAL OPEN
HardwareSettleCompleteGate OPEN
HsiAvailableGate CLOSED
DiagnosticsAvailableGate CLOSED
FirmwareReadyGate CLOSED
CardCommsDriverReadyGate CLOSED
ShelfCommsAvailableGate CLOSED
DataProvisioningCompletedGate 已关闭
命令于 2003 年 1 月 7 日星期二 - 23:53:38 (UTC) 完成
----/重启> ~ * 连接被外部主机关闭。
当我尝试新事物时,这可能会一直发生,也可能会在我现有的任何功能中间歇性发生。我上面发送的命令在触发此错误之前的几分钟内已经运行了 10 多次。
这给我和我的同事以及使用我的包裹的客户带来了很多麻烦。我们有数万行代码使用它,现在越来越多的客户使用我们的测试套件,问题变得越来越明显。用另一种语言重写是我们最后的选择。
解决方法:
所以我尝试增加超时时间,但始终不起作用。
我也尝试过连续两次期待(以防第一次超时),但即使中间有延迟也无济于事。这就像我必须在再次调用它之前离开调用 ::connections::telnet::Expect 的函数。
我开发了一个函数来清除程序通道缓冲区和期望的缓冲区(因为似乎期望库中还没有包含一个)。有时它有帮助,有时它没有。即使它确实有 100% 的帮助,它也不是每次我们调用 send 命令时都使用的可行选项,因为它会将每个命令的执行减慢到至少 1 秒的执行时间。这里是:
# ------------------------------------------------------------------------------
# Description: Clears a process's channel buffer and expect_out(buffer).
#
# Parameters: processID - in: Spawned process identifier.
#
# Return: void - returns no signicant value.
# ------------------------------------------------------------------------------
proc ::utils::ClearBuffer {processID} {
# Print function name and parameters
PrintDebug [info level 0]
# set expect parameters
set spawn_id $processID
set timeout 1
catch {expect -re "thischainwillmostcertainlyneverappear"}
catch {expect -re ".*"}
}
我的请求:
还有其他人遇到过这个问题吗?
为什么我的产品的输出不会像往常一样突然堆积在 expect 的缓冲区中?
是否有任何特殊字符可以告诉期望停止这样做?
还有什么我做错了吗?
还有其他解决方法的建议吗?
非常感谢您的宝贵时间。
【问题讨论】: