420 lines
11 KiB
Go
420 lines
11 KiB
Go
// +build !windows
|
|
|
|
package gexpect
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestEmptySearchString(t *testing.T) {
|
|
t.Logf("Testing empty search string...")
|
|
child, err := Spawn("echo Hello World")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = child.Expect("")
|
|
if err != ErrEmptySearch {
|
|
t.Fatalf("Expected empty search error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestHelloWorld(t *testing.T) {
|
|
t.Logf("Testing Hello World... ")
|
|
child, err := Spawn("echo \"Hello World\"")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = child.Expect("Hello World")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestDoubleHelloWorld(t *testing.T) {
|
|
t.Logf("Testing Double Hello World... ")
|
|
child, err := Spawn(`sh -c "echo Hello World ; echo Hello ; echo Hi"`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = child.Expect("Hello World")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = child.Expect("Hello")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = child.Expect("Hi")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestHelloWorldFailureCase(t *testing.T) {
|
|
t.Logf("Testing Hello World Failure case... ")
|
|
child, err := Spawn("echo \"Hello World\"")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = child.Expect("YOU WILL NEVER FIND ME")
|
|
if err != nil {
|
|
return
|
|
}
|
|
t.Fatal("Expected an error for TestHelloWorldFailureCase")
|
|
}
|
|
|
|
func TestBiChannel(t *testing.T) {
|
|
|
|
t.Logf("Testing BiChannel screen... ")
|
|
child, err := Spawn("cat")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sender, receiver := child.AsyncInteractChannels()
|
|
wait := func(str string) {
|
|
for {
|
|
msg, open := <-receiver
|
|
if !open {
|
|
return
|
|
}
|
|
if strings.Contains(msg, str) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
endlChar := fmt.Sprintln("")
|
|
sender <- fmt.Sprintf("echo%v", endlChar)
|
|
wait("echo")
|
|
sender <- fmt.Sprintf("echo2%v", endlChar)
|
|
wait("echo2")
|
|
child.Close()
|
|
child.Wait()
|
|
}
|
|
|
|
func TestCommandStart(t *testing.T) {
|
|
t.Logf("Testing Command... ")
|
|
|
|
// Doing this allows you to modify the cmd struct prior to execution, for example to add environment variables
|
|
child, err := Command("echo 'Hello World'")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
child.Start()
|
|
child.Expect("Hello World")
|
|
}
|
|
|
|
var regexMatchTests = []struct {
|
|
re string
|
|
good string
|
|
bad string
|
|
}{
|
|
{`a`, `a`, `b`},
|
|
{`.b`, `ab`, `ac`},
|
|
{`a+hello`, `aaaahello`, `bhello`},
|
|
{`(hello|world)`, `hello`, `unknown`},
|
|
{`(hello|world)`, `world`, `unknown`},
|
|
{"\u00a9", "\u00a9", `unknown`}, // 2 bytes long unicode character "copyright sign"
|
|
}
|
|
|
|
func TestRegexMatch(t *testing.T) {
|
|
t.Logf("Testing Regular Expression Matching... ")
|
|
for _, tt := range regexMatchTests {
|
|
runTest := func(input string) bool {
|
|
var match bool
|
|
child, err := Spawn("echo \"" + input + "\"")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
match, err = child.ExpectRegex(tt.re)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return match
|
|
}
|
|
if !runTest(tt.good) {
|
|
t.Errorf("Regex Not matching [%#q] with pattern [%#q]", tt.good, tt.re)
|
|
}
|
|
if runTest(tt.bad) {
|
|
t.Errorf("Regex Matching [%#q] with pattern [%#q]", tt.bad, tt.re)
|
|
}
|
|
}
|
|
}
|
|
|
|
var regexFindTests = []struct {
|
|
re string
|
|
input string
|
|
matches []string
|
|
}{
|
|
{`he(l)lo wo(r)ld`, `hello world`, []string{"hello world", "l", "r"}},
|
|
{`(a)`, `a`, []string{"a", "a"}},
|
|
{`so.. (hello|world)`, `so.. hello`, []string{"so.. hello", "hello"}},
|
|
{`(a+)hello`, `aaaahello`, []string{"aaaahello", "aaaa"}},
|
|
{`\d+ (\d+) (\d+)`, `123 456 789`, []string{"123 456 789", "456", "789"}},
|
|
{`\d+ (\d+) (\d+)`, "\u00a9 123 456 789 \u00a9", []string{"123 456 789", "456", "789"}}, // check unicode characters
|
|
}
|
|
|
|
func TestRegexFind(t *testing.T) {
|
|
t.Logf("Testing Regular Expression Search... ")
|
|
for _, tt := range regexFindTests {
|
|
runTest := func(input string) []string {
|
|
child, err := Spawn("echo \"" + input + "\"")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
matches, err := child.ExpectRegexFind(tt.re)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return matches
|
|
}
|
|
matches := runTest(tt.input)
|
|
if len(matches) != len(tt.matches) {
|
|
t.Fatalf("Regex not producing the expected number of patterns.. got[%d] ([%s]) expected[%d] ([%s])",
|
|
len(matches), strings.Join(matches, ","),
|
|
len(tt.matches), strings.Join(tt.matches, ","))
|
|
}
|
|
for i, _ := range matches {
|
|
if matches[i] != tt.matches[i] {
|
|
t.Errorf("Regex Expected group [%s] and got group [%s] with pattern [%#q] and input [%s]",
|
|
tt.matches[i], matches[i], tt.re, tt.input)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadLine(t *testing.T) {
|
|
t.Logf("Testing ReadLine...")
|
|
|
|
child, err := Spawn("echo \"foo\nbar\"")
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s, err := child.ReadLine()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s != "foo\r" {
|
|
t.Fatalf("expected 'foo\\r', got '%s'", s)
|
|
}
|
|
s, err = child.ReadLine()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s != "bar\r" {
|
|
t.Fatalf("expected 'bar\\r', got '%s'", s)
|
|
}
|
|
}
|
|
|
|
func TestRegexWithOutput(t *testing.T) {
|
|
t.Logf("Testing Regular Expression search with output...")
|
|
|
|
s := "You will not find me"
|
|
p, err := Spawn("echo -n " + s)
|
|
if err != nil {
|
|
t.Fatalf("Cannot exec rkt: %v", err)
|
|
}
|
|
searchPattern := `I should not find you`
|
|
result, out, err := p.ExpectRegexFindWithOutput(searchPattern)
|
|
if err == nil {
|
|
t.Fatalf("Shouldn't have found `%v` in `%v`", searchPattern, out)
|
|
}
|
|
if s != out {
|
|
t.Fatalf("Child output didn't match: %s", out)
|
|
}
|
|
|
|
err = p.Wait()
|
|
if err != nil {
|
|
t.Fatalf("Child didn't terminate correctly: %v", err)
|
|
}
|
|
|
|
p, err = Spawn("echo You will find me")
|
|
if err != nil {
|
|
t.Fatalf("Cannot exec rkt: %v", err)
|
|
}
|
|
searchPattern = `.*(You will).*`
|
|
result, out, err = p.ExpectRegexFindWithOutput(searchPattern)
|
|
if err != nil || result[1] != "You will" {
|
|
t.Fatalf("Did not find pattern `%v` in `%v'\n", searchPattern, out)
|
|
}
|
|
err = p.Wait()
|
|
if err != nil {
|
|
t.Fatalf("Child didn't terminate correctly: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRegexTimeoutWithOutput(t *testing.T) {
|
|
t.Logf("Testing Regular Expression search with timeout and output...")
|
|
|
|
seconds := 2
|
|
timeout := time.Duration(seconds-1) * time.Second
|
|
|
|
p, err := Spawn(fmt.Sprintf("sh -c 'sleep %d && echo You find me'", seconds))
|
|
if err != nil {
|
|
t.Fatalf("Cannot exec rkt: %v", err)
|
|
}
|
|
searchPattern := `find me`
|
|
result, out, err := p.ExpectTimeoutRegexFindWithOutput(searchPattern, timeout)
|
|
if err == nil {
|
|
t.Fatalf("Shouldn't have finished call with result: %v", result)
|
|
}
|
|
|
|
seconds = 2
|
|
timeout = time.Duration(seconds+1) * time.Second
|
|
|
|
p, err = Spawn(fmt.Sprintf("sh -c 'sleep %d && echo You find me'", seconds))
|
|
if err != nil {
|
|
t.Fatalf("Cannot exec rkt: %v", err)
|
|
}
|
|
searchPattern = `find me`
|
|
result, out, err = p.ExpectTimeoutRegexFindWithOutput(searchPattern, timeout)
|
|
if err != nil {
|
|
t.Fatalf("Didn't find %v in output: %v", searchPattern, out)
|
|
}
|
|
}
|
|
|
|
func TestRegexFindNoExcessBytes(t *testing.T) {
|
|
t.Logf("Testing Regular Expressions returning output with no excess strings")
|
|
repeats := 50
|
|
tests := []struct {
|
|
desc string
|
|
loopBody string
|
|
searchPattern string
|
|
expectFullTmpl string
|
|
unmatchedData string
|
|
}{
|
|
{
|
|
desc: `matching lines line by line with $ at the end of the regexp`,
|
|
loopBody: `echo "prefix: ${i} line"`,
|
|
searchPattern: `(?m)^prefix:\s+(\d+) line\s??$`,
|
|
expectFullTmpl: `prefix: %d line`,
|
|
unmatchedData: "\n",
|
|
// the "$" char at the end of regexp does not
|
|
// match the \n, so it is left as an unmatched
|
|
// data
|
|
},
|
|
{
|
|
desc: `matching lines line by line with \n at the end of the regexp`,
|
|
loopBody: `echo "prefix: ${i} line"`,
|
|
searchPattern: `(?m)^prefix:\s+(\d+) line\s??\n`,
|
|
expectFullTmpl: `prefix: %d line`,
|
|
unmatchedData: "",
|
|
},
|
|
{
|
|
desc: `matching chunks in single line chunk by chunk`,
|
|
loopBody: `printf "a ${i} b"`,
|
|
searchPattern: `a\s+(\d+)\s+b`,
|
|
expectFullTmpl: `a %d b`,
|
|
unmatchedData: "",
|
|
},
|
|
}
|
|
seqCmd := fmt.Sprintf("`seq 1 %d`", repeats)
|
|
shCmdTmpl := fmt.Sprintf(`sh -c 'for i in %s; do %%s; done'`, seqCmd)
|
|
for _, tt := range tests {
|
|
t.Logf("Test: %s", tt.desc)
|
|
shCmd := fmt.Sprintf(shCmdTmpl, tt.loopBody)
|
|
t.Logf("Running command: %s", shCmd)
|
|
p, err := Spawn(shCmd)
|
|
if err != nil {
|
|
t.Fatalf("Cannot exec shell script: %v", err)
|
|
}
|
|
defer func() {
|
|
if err := p.Wait(); err != nil {
|
|
t.Fatalf("shell script didn't terminate correctly: %v", err)
|
|
}
|
|
}()
|
|
for i := 1; i <= repeats; i++ {
|
|
matches, output, err := p.ExpectRegexFindWithOutput(tt.searchPattern)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get the match number %d: %v", i, err)
|
|
}
|
|
if len(matches) != 2 {
|
|
t.Fatalf("Expected only 2 matches, got %d", len(matches))
|
|
}
|
|
full := strings.TrimSpace(matches[0])
|
|
expFull := fmt.Sprintf(tt.expectFullTmpl, i)
|
|
partial := matches[1]
|
|
expPartial := fmt.Sprintf("%d", i)
|
|
if full != expFull {
|
|
t.Fatalf("Did not the expected full match %q, got %q", expFull, full)
|
|
}
|
|
if partial != expPartial {
|
|
t.Fatalf("Did not the expected partial match %q, got %q", expPartial, partial)
|
|
}
|
|
// The output variable usually contains the
|
|
// unmatched data followed by the whole match.
|
|
// The first line is special as it has no data
|
|
// preceding it.
|
|
var expectedOutput string
|
|
if i == 1 || tt.unmatchedData == "" {
|
|
expectedOutput = matches[0]
|
|
} else {
|
|
expectedOutput = fmt.Sprintf("%s%s", tt.unmatchedData, matches[0])
|
|
}
|
|
if output != expectedOutput {
|
|
t.Fatalf("The collected output %q should be the same as the whole match %q", output, expectedOutput)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBufferReadRune(t *testing.T) {
|
|
tests := []struct {
|
|
bufferContent []byte
|
|
fileContent []byte
|
|
expectedRune rune
|
|
}{
|
|
// unicode "copyright char" is \u00a9 is encoded as two bytes in utf8 0xc2 0xa9
|
|
{[]byte{0xc2, 0xa9}, []byte{}, '\u00a9'}, // whole rune is already in buffer.b
|
|
{[]byte{0xc2}, []byte{0xa9}, '\u00a9'}, // half of is in the buffer.b and another half still in buffer.f (file)
|
|
{[]byte{}, []byte{0xc2, 0xa9}, '\u00a9'}, // whole rune is the file
|
|
// some random noise in the end of file
|
|
{[]byte{0xc2, 0xa9}, []byte{0x20, 0x20, 0x20, 0x20}, '\u00a9'},
|
|
{[]byte{0xc2}, []byte{0xa9, 0x20, 0x20, 0x20, 0x20}, '\u00a9'},
|
|
{[]byte{}, []byte{0xc2, 0xa9, 0x20, 0x20, 0x20, 0x20}, '\u00a9'},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
|
|
// prepare tmp file with fileContent
|
|
f, err := ioutil.TempFile("", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n, err := f.Write(tt.fileContent)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != len(tt.fileContent) {
|
|
t.Fatal("expected fileContent written to temp file")
|
|
}
|
|
_, err = f.Seek(0, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// new buffer
|
|
buf := buffer{f: f, b: *bytes.NewBuffer(tt.bufferContent)}
|
|
|
|
// call ReadRune
|
|
r, size, err := buf.ReadRune()
|
|
|
|
if r != tt.expectedRune {
|
|
t.Fatalf("#%d: expected rune %+q but go is %+q", i, tt.expectedRune, r)
|
|
}
|
|
|
|
if size != len(string(tt.expectedRune)) {
|
|
t.Fatalf("#%d: expected rune %d bytes long but got just %d bytes long", i, len(string(tt.expectedRune)), size)
|
|
}
|
|
|
|
}
|
|
|
|
}
|