-
Notifications
You must be signed in to change notification settings - Fork 1
/
readline_test.go
136 lines (114 loc) · 2.69 KB
/
readline_test.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package cli
import (
"context"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestReadLine(t *testing.T) {
defer func() { stdin = os.Stdin }()
ctx := context.Background()
stdin = strings.NewReader("This is a test line\n")
line := ReadLine(ctx)
assert.Equal(t, "This is a test line", line)
}
func TestReadLine_BlockContext(t *testing.T) {
defer func() { stdin = os.Stdin }()
ctx, cancel := context.WithCancel(context.Background())
sync := make(chan bool)
r := blockingReader{input: make(chan string, 1), omitNewLine: true}
stdin = r
r.input <- "wait until the user hits enter"
var line string
go func() {
<-sync
line = ReadLine(ctx)
<-sync
}()
sync <- true // start the goroutine
select {
case sync <- true:
t.Fatal("ReadLine should block until the first \\n but it returned")
case <-time.After(5 * time.Millisecond):
// ok good, time to move on
}
cancel()
select {
case sync <- true:
assert.Empty(t, line)
case <-time.After(5 * time.Millisecond):
t.Fatal("ReadLine should return if the given context is canceled")
}
}
func TestReadLines(t *testing.T) {
defer func() { stdin = os.Stdin }()
ctx := context.Background()
cases := map[string][]string{
"empty input": nil,
"one line": {
"line 1\n",
},
"three lines": {
"line 1\n",
"line 2\n",
"line 3\n",
},
}
for name, input := range cases {
t.Run(name, func(t *testing.T) {
t.Log(name)
stdin = strings.NewReader(strings.Join(input, ""))
linesChan := ReadLines(ctx)
lines := extract(linesChan)
for i, expected := range input {
expected = expected[:len(expected)-1] // expect string without trailing newline
assert.Equal(t, expected, lines[i])
}
})
}
}
func TestReadLinesCancel(t *testing.T) {
defer func() { stdin = os.Stdin }()
ctx, cancel := context.WithCancel(context.Background())
r := blockingReader{input: make(chan string, 1)}
r.input <- "line1"
stdin = r
linesChan := ReadLines(ctx)
<-linesChan
cancel()
select {
case _, ok := <-linesChan:
assert.False(t, ok, "channel should have been closed when context is canceled")
case <-time.After(100 * time.Millisecond):
t.Error("timeout: seems like the channel was not closed")
}
}
func extract(c <-chan string) []string {
result := make(chan []string)
go func() {
var lines []string
for s := range c {
lines = append(lines, s)
}
result <- lines
}()
select {
case r := <-result:
return r
case <-time.After(time.Second):
panic("timeout")
}
}
type blockingReader struct {
input chan string
omitNewLine bool
}
func (r blockingReader) Read(p []byte) (int, error) {
s := <-r.input
if !r.omitNewLine {
s = s + "\n"
}
return strings.NewReader(s).Read(p)
}