expect

package module
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 1, 2020 License: Apache-2.0 Imports: 17 Imported by: 2

README

go-expect

Package expect provides an expect-like interface to automate control of applications. It is unlike expect in that it does not spawn or manage process lifecycle. This package only focuses on expecting output and sending input through it's pseudoterminal.

This is a fork of the original repository Netflix/go-expect mostly to add Windows support. This fork has been added to test the ActiveState state tool

Relevant additions:

  • Windows support (Windows 10 and Windows Sever 2019 only)
  • expect.Console is created with xpty allowing testing of applications that want to talk to an xterm-compatible terminal
  • Filter out VT control characters in output. This is important for Windows support, as the windows pseudo-console creates lots of control-characters that can break up words.

See also ActiveState/termtest for a library that uses this package, but adds more life-cycle management.

Usage

os.Exec example
package main

import (
	"log"
	"os"
	"os/exec"
	"time"

	"github.com/ActiveState/termtest/expect"
)

func main() {
	c, err := expect.NewConsole(expect.WithStdout(os.Stdout))
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	cmd := exec.Command("vi")
	cmd.Stdin = c.Tty()
	cmd.Stdout = c.Tty()
	cmd.Stderr = c.Tty()

	go func() {
		c.ExpectEOF()
	}()

	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	time.Sleep(time.Second)
	c.Send("iHello world\x1b")
	time.Sleep(time.Second)
	c.Send("dd")
	time.Sleep(time.Second)
	c.SendLine(":q!")

	err = cmd.Wait()
	if err != nil {
		log.Fatal(err)
	}
}
golang.org/x/crypto/ssh/terminal example
package main

import (
	"fmt"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/ActiveState/termtest/expect"
)

func getPassword(fd int) string {
	bytePassword, _ := terminal.ReadPassword(fd)

	return string(bytePassword)
}

func main() {
	c, _ := expect.NewConsole()

	defer c.Close()

	donec := make(chan struct{})
	go func() {
		defer close(donec)
		c.SendLine("hunter2")
	}()

	echoText := getPassword(int(c.Tty().Fd()))

	<-donec

	fmt.Printf("\nPassword from stdin: %s", echoText)
}

Documentation

Overview

Package expect provides an expect-like interface to automate control of applications. It is unlike expect in that it does not spawn or manage process lifecycle. This package only focuses on expecting output and sending input through it's psuedoterminal.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EOF

func EOF(opts *ExpectOpts) error

EOF adds an Expect condition to exit if io.EOF is returned from reading Console's tty.

func NewTestWriter

func NewTestWriter(t *testing.T) (io.Writer, error)

NewTestWriter returns an io.Writer where bytes written to the file are logged by go's testing logger. Bytes are flushed to the logger on line end.

func PTSClosed

func PTSClosed(opts *ExpectOpts) error

PTSClosed adds an Expect condition to exit if we get an "read /dev/ptmx: input/output error" error which can occur on Linux while reading from the ptm after the pts is closed. Further Reading: https://github.com/kr/pty/issues/21#issuecomment-129381749

func StdinClosed

func StdinClosed(opts *ExpectOpts) error

StdinClosed adds an Expect condition to exit if we read from stdin after it has been closed which can occur on Windows while reading from the Pseudo-terminal after it is closed

func StripTrailingEmptyLines

func StripTrailingEmptyLines(out string) string

StripTrailingEmptyLines returns a copy of s stripped of trailing lines that consist of only space characters.

Types

type CallbackMatcher

type CallbackMatcher interface {
	// Callback executes the matcher's callback with the terminal state at the
	// time of match.
	Callback(matchState *MatchState) error
}

CallbackMatcher is a matcher that provides a Callback function.

type Console

type Console struct {
	Pty        *xpty.Xpty
	MatchState *MatchState
	// contains filtered or unexported fields
}

Console is an interface to automate input and output for interactive applications. Console can block until a specified output is received and send input back on it's tty. Console can also multiplex other sources of input and multiplex its output to other writers.

func NewConsole

func NewConsole(opts ...ConsoleOpt) (*Console, error)

NewConsole returns a new Console with the given options.

func NewTestConsole

func NewTestConsole(t *testing.T, opts ...ConsoleOpt) (*Console, error)

NewTestConsole returns a new Console that multiplexes the application's stdout to go's testing logger. Primarily so that outputs from parallel tests using t.Parallel() is not interleaved.

func (*Console) Close

func (c *Console) Close() error

Close closes both the TTY and afterwards all the readers You may want to split this up to give the readers time to read all the data until they reach the EOF error

func (*Console) CloseReaders

func (c *Console) CloseReaders() (err error)

CloseReaders closes everything that is trying to read from the terminal Call this function once you are sure that you have consumed all bytes

func (*Console) Expect

func (c *Console) Expect(opts ...ExpectOpt) (string, error)

Expect reads from Console's tty until a condition specified from opts is encountered or an error occurs, and returns the buffer read by console. No extra bytes are read once a condition is met, so if a program isn't expecting input yet, it will be blocked. Sends are queued up in tty's internal buffer so that the next Expect will read the remaining bytes (i.e. rest of prompt) as well as its conditions.

func (*Console) ExpectEOF

func (c *Console) ExpectEOF() (string, error)

ExpectEOF reads from Console's tty until EOF or an error occurs, and returns the buffer read by Console. We also treat the PTSClosed error as an EOF.

func (*Console) ExpectLongString added in v0.5.6

func (c *Console) ExpectLongString(s string) (string, error)

ExpectLongString reads from Console's tty until the provided long string is read or an error occurs, and returns the buffer read by Console. This function ignores mismatches caused by newline and space characters to account for wrappings at the maximum terminal width.

func (*Console) ExpectString

func (c *Console) ExpectString(s string) (string, error)

ExpectString reads from Console's tty until the provided string is read or an error occurs, and returns the buffer read by Console.

func (*Console) Expectf

func (c *Console) Expectf(format string, args ...interface{}) (string, error)

Expectf reads from the Console's tty until the provided formatted string is read or an error occurs, and returns the buffer read by Console.

func (*Console) Fd

func (c *Console) Fd() uintptr

Fd returns Console's file descripting referencing the master part of its pty.

func (*Console) Log

func (c *Console) Log(v ...interface{})

Log prints to Console's logger. Arguments are handled in the manner of fmt.Print.

func (*Console) Logf

func (c *Console) Logf(format string, v ...interface{})

Logf prints to Console's logger. Arguments are handled in the manner of fmt.Printf.

func (*Console) Send

func (c *Console) Send(s string) (int, error)

Send writes string s to Console's tty.

func (*Console) SendLine

func (c *Console) SendLine(s string) (int, error)

SendLine writes string s to Console's tty with a trailing newline.

func (*Console) SendOSLine added in v0.7.0

func (c *Console) SendOSLine(s string) (int, error)

SendOSLine writes string s to Console's tty with a trailing newline separator native to the base OS.

func (*Console) Tty

func (c *Console) Tty() *os.File

Tty returns Console's pts (slave part of a pty). A pseudoterminal, or pty is a pair of pseudo-devices, one of which, the slave, emulates a real text terminal device.

func (*Console) Write

func (c *Console) Write(b []byte) (int, error)

Write writes bytes b to Console's tty.

type ConsoleCallback

type ConsoleCallback func(ms *MatchState) error

ConsoleCallback is a callback function to execute if a match is found for the chained matcher.

type ConsoleOpt

type ConsoleOpt func(*ConsoleOpts) error

ConsoleOpt allows setting Console options.

func WithCloser

func WithCloser(closer ...io.Closer) ConsoleOpt

WithCloser adds closers that are closed in order when Console is closed.

func WithDefaultTimeout

func WithDefaultTimeout(timeout time.Duration) ConsoleOpt

WithDefaultTimeout sets a default read timeout during Expect statements.

func WithExpectObserver

func WithExpectObserver(observers ...ExpectObserver) ConsoleOpt

WithExpectObserver adds an ExpectObserver to allow monitoring Expect operations.

func WithLogger

func WithLogger(logger *log.Logger) ConsoleOpt

WithLogger adds a logger for Console to log debugging information to. By default Console will discard logs.

func WithSendObserver

func WithSendObserver(observers ...SendObserver) ConsoleOpt

WithSendObserver adds a SendObserver to allow monitoring Send operations.

func WithStdin

func WithStdin(readers ...io.Reader) ConsoleOpt

WithStdin adds readers that bytes read are written to Console's tty. If a listed reader returns an error, that reader will not be continued to read.

func WithStdout

func WithStdout(writers ...io.Writer) ConsoleOpt

WithStdout adds writers that Console duplicates writes to, similar to the Unix tee(1) command.

Each write is written to each listed writer, one at a time. Console is the last writer, writing to it's internal buffer for matching expects. If a listed writer returns an error, that overall write operation stops and returns the error; it does not continue down the list.

func WithTermCols added in v0.5.6

func WithTermCols(cols int) ConsoleOpt

WithTermCols sets the number of columns in the terminal (Default: 80)

func WithTermRows added in v0.5.6

func WithTermRows(rows int) ConsoleOpt

WithTermRows sets the number of rows in the terminal (Default: 80)

type ConsoleOpts

type ConsoleOpts struct {
	Logger          *log.Logger
	Stdins          []io.Reader
	Stdouts         []io.Writer
	Closers         []io.Closer
	ExpectObservers []ExpectObserver
	SendObservers   []SendObserver
	ReadTimeout     *time.Duration
	TermCols        int
	TermRows        int
}

ConsoleOpts provides additional options on creating a Console.

type ExpectObserver

type ExpectObserver func(matchers []Matcher, ms *MatchState, err error)

ExpectObserver provides an interface for a function callback that will be called after each Expect operation. matchers will be the list of active matchers when an error occurred,

or a list of matchers that matched `buf` when err is nil.

buf is the captured output that was matched against. err is error that might have occurred. May be nil.

type ExpectOpt

type ExpectOpt func(*ExpectOpts) error

ExpectOpt allows settings Expect options.

func All

func All(expectOpts ...ExpectOpt) ExpectOpt

All adds an Expect condition to exit if the content read from Console's tty matches all of the provided ExpectOpt, in any order.

func Any

func Any(expectOpts ...ExpectOpt) ExpectOpt

Any adds an Expect condition to exit if the content read from Console's tty matches any of the provided ExpectOpt

func Error

func Error(errs ...error) ExpectOpt

Error adds an Expect condition to exit if reading from Console's tty returns one of the provided errors.

func LongString added in v0.5.6

func LongString(strs ...string) ExpectOpt

LongString adds an Expect condition to exit if the content read from Console's tty contains any of the given long strings ignoring newlines and spaces to account for potential automatic wrappings at the terminal width.

func Regexp

func Regexp(res ...*regexp.Regexp) ExpectOpt

Regexp adds an Expect condition to exit if the content read from Console's tty matches the given Regexp.

func RegexpPattern

func RegexpPattern(ps ...string) ExpectOpt

RegexpPattern adds an Expect condition to exit if the content read from Console's tty matches the given Regexp patterns. Expect returns an error if the patterns were unsuccessful in compiling the Regexp.

func String

func String(strs ...string) ExpectOpt

String adds an Expect condition to exit if the content read from Console's tty contains any of the given strings.

func WithTimeout

func WithTimeout(timeout time.Duration) ExpectOpt

WithTimeout sets a read timeout for an Expect statement.

func (ExpectOpt) Then

func (eo ExpectOpt) Then(f ConsoleCallback) ExpectOpt

Then returns an Expect condition to execute a callback if a match is found for the chained matcher.

type ExpectOpts

type ExpectOpts struct {
	Matchers    []Matcher
	ReadTimeout *time.Duration
}

ExpectOpts provides additional options on Expect.

func (ExpectOpts) Match

func (eo ExpectOpts) Match(v interface{}) Matcher

Match sequentially calls Match on all matchers in ExpectOpts and returns the first matcher if a match exists, otherwise nil.

type MatchState

type MatchState struct {
	// TermState is the current terminal state
	TermState *vt10x.State
	// Buf is a buffer of the raw characters parsed since the last match
	Buf *bytes.Buffer
	// contains filtered or unexported fields
}

MatchState describes the state of the terminal while trying to match it against an expectation

func (*MatchState) UnwrappedStringToCursorFromMatch

func (ms *MatchState) UnwrappedStringToCursorFromMatch(n int) string

UnwrappedStringToCursorFromMatch returns the parsed string from the position of the n-last match to the cursor position Terminal EOL-wrapping is removed

type Matcher

type Matcher interface {
	// Match returns true iff a match is found.
	Match(v interface{}) bool
	Criteria() interface{}
}

Matcher provides an interface for finding a match in content read from Console's tty.

type SendObserver

type SendObserver func(msg string, num int, err error)

SendObserver provides an interface for a function callback that will be called after each Send operation. msg is the string that was sent. num is the number of bytes actually sent. err is the error that might have occurred. May be nil.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL