-
Notifications
You must be signed in to change notification settings - Fork 15
/
expect.go
122 lines (111 loc) · 2.94 KB
/
expect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/yuin/gopher-lua"
"github.com/hymkor/expect/internal/go-console/output"
)
var useStderrOnGetRecentOutput = false
func getRecentOutputByStdoutOrStderr(captureLines int) ([]string, error) {
for {
if useStderrOnGetRecentOutput {
result, err := consoleoutput.GetRecentOutputByStderr(captureLines)
return result, err
}
result, err := consoleoutput.GetRecentOutputByStdout(captureLines)
if err == nil {
return result, nil
}
useStderrOnGetRecentOutput = true
}
}
type Matching struct {
Position int
Line string
Match string
PreMatch string
PostMatch string
}
func expect(ctx context.Context, keywords []string, timeover time.Duration, captureLines int) (int, *Matching, error) {
tick := time.NewTicker(time.Second / 10)
defer tick.Stop()
timer := time.NewTimer(timeover)
defer timer.Stop()
for {
outputs, err := getRecentOutputByStdoutOrStderr(captureLines)
if err != nil {
return -1, nil, fmt.Errorf("expect: %w", err)
}
for _, output := range outputs {
for i, str := range keywords {
if pos := strings.Index(output, str); pos >= 0 {
return i, &Matching{
Position: pos,
Line: output,
Match: output[pos : pos+len(str)],
PreMatch: output[:pos],
PostMatch: output[pos+len(str):],
}, nil
}
}
}
select {
case <-ctx.Done():
return -1, nil, fmt.Errorf("expect: %w", ctx.Err())
case <-timer.C:
return -1, nil, fmt.Errorf("expect: %w", context.DeadlineExceeded)
case <-tick.C:
}
}
}
const (
errnoExpectGetRecentOutput = -1
errnoExpectTimeOut = -2
errnoExpectContextDone = -3
)
// Expect is the implement of the lua-function `expect`
func Expect(L *lua.LState) int {
n := L.GetTop()
keywords := make([]string, n)
for i := 1; i <= n; i++ {
keywords[i-1] = L.ToString(i)
}
timeout := time.Hour
if n, ok := L.GetGlobal("timeout").(lua.LNumber); ok {
timeout = time.Duration(n) * time.Second
}
captureLines := 2
if n, ok := L.GetGlobal("capturelines").(lua.LNumber); ok {
captureLines = int(n)
}
rc, info, err := expect(L.Context(), keywords, timeout, captureLines)
if err != nil {
if errors.Is(err, context.Canceled) {
rc = errnoExpectContextDone
} else if errors.Is(err, context.DeadlineExceeded) {
rc = errnoExpectTimeOut
} else {
rc = errnoExpectGetRecentOutput
fmt.Fprintln(os.Stderr, err.Error())
}
}
if info != nil {
L.SetGlobal("_MATCHPOSITION", lua.LNumber(info.Position))
L.SetGlobal("_MATCHLINE", lua.LString(info.Line))
L.SetGlobal("_MATCH", lua.LString(info.Match))
L.SetGlobal("_PREMATCH", lua.LString(info.PreMatch))
L.SetGlobal("_POSTMATCH", lua.LString(info.PostMatch))
} else {
L.SetGlobal("_MATCHPOSITION", lua.LNil)
L.SetGlobal("_MATCHLINE", lua.LNil)
L.SetGlobal("_MATCH", lua.LNil)
L.SetGlobal("_PREMATCH", lua.LNil)
L.SetGlobal("_POSTMATCH", lua.LNil)
}
L.Push(lua.LNumber(rc))
return 1
}