-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
scanners_test.go
121 lines (107 loc) · 3.14 KB
/
scanners_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
package slacknimate
import (
"context"
"strings"
"testing"
"time"
)
func TestLineScanner(t *testing.T) {
letters := strings.Split("abcdefghijklmnopqrstuvwxyz", "")
alphabet := strings.Join(letters, "\n")
t.Run("normal", func(t *testing.T) {
r := strings.NewReader(alphabet)
s := NewLineScanner(context.Background(), r)
// verify each frame
//
// channel will close upon completion, if not the range will never
// conclude and the test will fail via timeout.
var i int
for frame := range s.Frames() {
if want := letters[i]; frame != want {
t.Errorf("frame %d: want %v got %v", i, want, frame)
}
i++
}
// check err
if err := s.Err(); err != nil {
t.Errorf("Err(): want %v got %v", nil, err)
}
})
t.Run("context expired", func(t *testing.T) {
ctx, cf := context.WithCancel(context.Background())
r := strings.NewReader(alphabet)
s := NewLineScanner(ctx, r)
// read to roughly the halfway point
frames := s.Frames()
for i := 0; i <= len(letters)/2; i++ {
_, ok := <-frames
if !ok {
t.Fatal("channel closed prematurely")
}
}
// oh no! someone just cancelled our context!
cf()
// allow context cancellation time to propagate across goroutines
<-time.After(time.Millisecond)
// and now the channel should be closed
if _, ok := <-s.Frames(); ok {
t.Fatal("expected closed channel")
}
})
}
func TestLoopingLineScanner(t *testing.T) {
t.Run("normal", func(t *testing.T) {
ctx, cancelFunc := context.WithCancel(context.Background())
// create new LLS scanning a source with multiple lines
chunks := []string{
"Mary had a litte lamb.",
"It's fleece was white as snow.",
"And everywhere that Mary went,",
"The lamb was sure to go.",
}
r := strings.NewReader(strings.Join(chunks, "\n"))
s := NewLoopingLineScanner(ctx, r, len(chunks)*2)
// read for 5 full iterations, channel should not be closed
frames := s.Frames()
for i := 0; i < 5*len(chunks); i++ {
got, ok := <-frames
if !ok {
t.Fatalf("channel closed before expected on iteration %d", i)
}
want := chunks[i%len(chunks)]
if want != got {
t.Errorf("unexpected frame contents: want %v got %v", want, got)
}
}
// after which, Err() should still be nil
if err := s.Err(); err != nil {
t.Errorf("unexpected Err(): %v", err)
}
// cancel the context, make sure channel got closed
cancelFunc()
<-time.After(time.Millisecond)
_, ok := <-frames
if ok {
t.Fatal("channel not closed after context cancelled")
}
// scanner should have received the context cancellation as err
wantErr := context.Canceled
if gotErr := s.Err(); gotErr != wantErr {
t.Errorf("Err(): want %v got %v", wantErr, gotErr)
}
})
t.Run("buffer size exceeded", func(t *testing.T) {
tenXs := "x\nx\nx\nx\nx\nx\nx\nx\nx\nx"
r := strings.NewReader(tenXs)
s := NewLoopingLineScanner(context.Background(), r, 8)
// frames channel should be closed before output begins
_, ok := <-s.Frames()
if ok {
t.Error("channel not closed after buffer size exceeded")
}
wantErr := ErrMaxFramesExceeded
if gotErr := s.Err(); gotErr != wantErr {
t.Errorf("Err(): want %v got %v", wantErr, gotErr)
}
})
}