TCL / Expect - 预期时输出未显示在缓冲区中

时间:2015-02-19 19:02:26

标签: tcl buffer expect

好吧,所以我在一家电信公司工作,为我的产品开发了一些自动化测试。我正在使用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\"."
        }
    }
}

基本上,您可以使用以下功能:

  • ::连接::的telnet ::连接
    • 使用telnet连接到IP和端口,并期待某个提示。
  • ::连接::的telnet ::断开
    • 关闭telnet。
  • ::连接::的telnet :: SendCommand
    • 通过telnet发送命令,并在规定的时间内预期某个提示。
  • ::连接::的telnet ::期待
    • 在指定的时间内预期某个提示而不发送命令。

问题:

有时会出现一个错误,我不知道是什么造成的。有时我使用SendCommand并且没有输出存储在expect缓冲区中,因此显然该函数超时并引发异常。除非我期待第二次,否则我们可以清楚地看到(在我的日志中)缓冲区现在确实包含我之前调用所需的输出而不是超时。

以下是我的一些调试日志,以显示它的外观:

Feb / 18/2015 18:16:17 :: :: connections :: telnet :: SendCommand exp5 {~re restart dispGates} {\ n} {----。*> 10缓冲区

2015年2月18日18:16:17:期待exp5 {----。*> 10缓冲区

2015年2月18日18:16:27: 这是记录缓冲区内容的地方,不应为空

2015年2月18日18:16:27:在等待“----。*>”时超时。

...

Feb / 18/2015 18:16:28 ::: connections :: telnet :: SendCommand exp5 {〜*} {\ n} {连接被外国主机关闭。} 30

2015年2月18日18:16:29:期待exp5 {连接被外国主机关闭。} 30缓冲区

这是我期待最后一次电话预期的输出!您可以清楚地看到我在此发送的两个命令。

2015年2月18日18:16:30:〜重启dispGates

            RST_GATE_INITIAL     OPEN

  HardwareSettleCompleteGate     OPEN

            HsiAvailableGate     CLOSED

    DiagnosticsAvailableGate     CLOSED

           FirmwareReadyGate     CLOSED

    CardCommsDriverReadyGate     CLOSED

     ShelfCommsAvailableGate     CLOSED

DataProvisioningCompletedGate CLOSED

命令于2003年1月7日 - 23:53:38(UTC)完成

---- /重启> 〜* 连接由外国主机关闭。

当我尝试新的东西时,这种情况可能会持续发生,或者我可以使用任何现有功能间歇性地发生这种情况。我上面发送的命令在几分钟之内已经工作了10次以上,然后才触发这个错误。

这给我以及使用我的软件包的同事和客户带来了很多麻烦。我们有数以万计的代码行使用它,现在越来越多的客户使用我们的测试套装,问题变得越来越明显。用另一种语言重写是我们最后的选择。

解决方法:

所以我尝试增加超时,但这永远不会奏效。

我也尝试过连续两次(如果第一次出局),但即使延迟时间也没有帮助。这就像我必须在再次调用之前将函数调用:: connections :: telnet :: Expect。

我开发了一个函数来清除程序通道缓冲区和期望的缓冲区(因为似乎没有一个已经包含在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 ".*"}
}

我的请求:

还有其他人遇到过此问题吗?

为什么我的产品的输出突然不会像往常一样在期望的缓冲区内累积?

是否有任何特殊字符可能会告诉您不要这样做?

还有别的我做错了吗?

有关解决方法的其他任何建议吗?

非常感谢您的时间。

1 个答案:

答案 0 :(得分:2)

每当我遇到这种问题时,我会首先使用exp_internal命令,该命令会转储大量有关缓冲区状态以及模式匹配方式的非常有用的信息。