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

TCL/Expect-在期望时输出未显示在缓冲区中,tcl,buffer,expect,Tcl,Buffer,Expect,好的,我在一家电信公司工作,我为该公司开发了一些针对我们产品的自动化测试。我正在使用TCL8.4和Expect 5.44.1.15,这是我被录用时被要求的。大多数测试包括使用telnet与产品外壳交互。因此,我提出了以下函数: # ------------------------------------------------------------------------------ # Description: Launch telnet session with specified

好的,我在一家电信公司工作,我为该公司开发了一些针对我们产品的自动化测试。我正在使用TCL8.4和Expect 5.44.1.15,这是我被录用时被要求的。大多数测试包括使用telnet与产品外壳交互。因此,我提出了以下函数:

    # ------------------------------------------------------------------------------
# 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::send命令
    • 通过telnet发送命令并期望在分配的时间内得到某个提示
  • ::连接::telnet::预期
    • 在不发送命令的情况下,在分配的时间内预期某个提示
问题:

有时会出现一个bug,我不知道是什么导致了它。有时我使用SendCommand,expect缓冲区中没有存储任何输出,因此显然函数超时并抛出异常。除了如果我期待第二次调用,那么我们可以清楚地看到(在我的日志中)缓冲区现在确实包含了我上次调用不超时所需的输出

以下是我的一些调试日志,向您展示了它的外观:

2015年2月18日18:16:17:::connections::telnet::SendCommand exp5{~restart dispGates}{\n}{--.*>}10缓冲区

2015年2月18日18:16:17:预期exp5{--.*>}10缓冲区

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

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

2015年2月18日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已关闭

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

----/重新启动>~* 连接被外部主机关闭

当我尝试新功能时,这种情况可能会持续发生,也可能在我现有的任何功能中间歇性发生。我上面发送的命令在触发此bug之前的几分钟内已经运行了10多次

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

解决方法:

所以我试着增加超时时间,但这根本不起作用

我也试着连续两次期待(如果第一次期待超时),但即使在两次之间有延迟也没有帮助。就像我必须在再次调用之前离开调用::connections::telnet::Expect的函数一样

我开发了一个函数来清除程序通道缓冲区和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 ".*"}
}
我的请求:

还有其他人遇到过这个问题吗

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

有没有什么特殊的角色会告诉你,你希望停止这样做

我还有什么地方做错了吗

还有其他解决办法的建议吗


非常感谢您抽出时间。

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

如果错误可以随意复制,这可能会很有用,但不幸的是,这非常罕见。大部分时间,它所做的只是阻止我的客户监控测试结果。无论出于何种原因,expect开发人员决定使用-f选项复制输出,而不是重定向输出。是否有办法防止此输出显示到脚本中的终端?谢谢你的帮助。如果使用-f选项,则无论指定0还是1作为值,它都会记录信息。在我看来,这似乎有悖常理,但它确实有效。