Netflix/go-expect

Example of using expect with a function instead of exec

petems opened this issue · 3 comments

Hi, awesome project!

I'm looking to run go-expect against a function, instead of an os.Exec() object.

Most of the examples I've found for go-expect online are either testing or using os.Exec()

For example, I have the following code:

package main

import (
  "bufio"
  "fmt"
  "io"
  "os"
)

func feeder(stdin io.Reader) (string) {

  scanner := bufio.NewScanner(stdin)
  var text string

  fmt.Print("Enter some text: ")
  scanner.Scan()
  text = scanner.Text()

  return text
}

func main() {
  text := feeder(os.Stdin)

  fmt.Print(text + "\n")
}

So all the function does is take input from stdin and echo it back to you.

So, how do I run that with go-expect?

I'm a newb to Golang so if this is weird forgive me, I'm feeling in the dark using other examples I can find.

I've got this far:

package main

import (
  "bufio"
  "fmt"
  "io"
  "bytes"

  "time"
  expect "github.com/Netflix/go-expect"
)

func feeder(stdin io.Reader) (string) {

  scanner := bufio.NewScanner(stdin)
  var text string

  fmt.Print("Enter some text: ")
  scanner.Scan()
  text = scanner.Text()

  return text
}

func main() {
  buf := new(bytes.Buffer)
  c, _ := expect.NewConsole(expect.WithStdout(buf))

  defer c.Close()

  donec := make(chan struct{})
  go func() {
    defer close(donec)
    time.Sleep(time.Second)
    c.Send("Hello world")
    time.Sleep(time.Second)
    c.ExpectEOF()
  }()

  echoText := feeder(c.Tty())

  <-donec

  fmt.Print("Reponse", echoText)
}

But this just waits forever and doesn't give "Hello World" to the stdin.

If you can help me get this working, I'd be happy to make a docs PR to add to the readme with a function example 😄

hi @petems , thanks for your interest in this project!

Your example does in fact receive the response correctly, but you're waiting for donec to close after c.ExpectEOF() unblocks. Since your function doesn't write to the expect console, and also doesn't produce a EOF, the goroutine will block indefinitely.

I've modified your example a bit:

package main

import (
	"bufio"
	"fmt"
	"io"

	expect "github.com/Netflix/go-expect"
)

func feeder(stdin io.Reader) string {
	scanner := bufio.NewScanner(stdin)
	var text string

	fmt.Print("Enter some text: ")
	scanner.Scan()
	text = scanner.Text()

	return text
}

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

	defer c.Close()

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

	echoText := feeder(c.Tty())

	<-donec

	fmt.Printf("\nResponse: %s", echoText)
}

This is the output:

~
❯ go run petems.go
Enter some text:
Response: Hello world

The reason why you don't see Hello world next to Enter some text is because on terminals the tty is in a mode to echo stdin back so you can see what you type. Since yours is a golang function, you'll have to print out scanned input back to the expect console in order to achieve the same effect.

Awesome, thanks so much that works perfectly! 😄

Opening PR with terminal example #12