Skip to content

Commit 2e83eff

Browse files
authored
Merge pull request #38 from Icinga/test-com
Test com/
2 parents 93f4d7e + 3a22782 commit 2e83eff

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed

com/com_test.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package com
2+
3+
import (
4+
"context"
5+
"github.com/stretchr/testify/require"
6+
"golang.org/x/sync/errgroup"
7+
"io"
8+
"testing"
9+
"time"
10+
)
11+
12+
func TestWaitAsync(t *testing.T) {
13+
subtests := []struct {
14+
name string
15+
input WaiterFunc
16+
error error
17+
}{
18+
{"no_error", func() error { return nil }, nil},
19+
{"error", func() error { return io.EOF }, io.EOF},
20+
{"sleep_no_error", func() error { time.Sleep(time.Second / 2); return nil }, nil},
21+
{"sleep_error", func() error { time.Sleep(time.Second / 2); return io.EOF }, io.EOF},
22+
}
23+
24+
for _, st := range subtests {
25+
t.Run(st.name, func(t *testing.T) {
26+
errs := WaitAsync(st.input)
27+
require.NotNil(t, errs)
28+
29+
if st.error != nil {
30+
select {
31+
case e, ok := <-errs:
32+
if !ok {
33+
require.Fail(t, "channel should not be closed, yet")
34+
}
35+
36+
require.Equal(t, st.error, e)
37+
case <-time.After(time.Second):
38+
require.Fail(t, "channel should not block")
39+
}
40+
}
41+
42+
select {
43+
case _, ok := <-errs:
44+
if ok {
45+
require.Fail(t, "channel should be closed")
46+
}
47+
case <-time.After(time.Second):
48+
require.Fail(t, "channel should not block")
49+
}
50+
})
51+
}
52+
}
53+
54+
func TestErrgroupReceive(t *testing.T) {
55+
subtests := []struct {
56+
name string
57+
input []error
58+
error bool
59+
}{
60+
{"nothing", nil, false},
61+
{"nil", []error{nil}, false},
62+
{"non-nil", []error{io.EOF}, true},
63+
}
64+
65+
latencies := []struct {
66+
name string
67+
latency time.Duration
68+
}{
69+
{"instantly", 0},
70+
{"1us", time.Microsecond},
71+
{"20ms", 20 * time.Millisecond},
72+
}
73+
74+
for _, st := range subtests {
75+
t.Run(st.name, func(t *testing.T) {
76+
for _, l := range latencies {
77+
t.Run(l.name, func(t *testing.T) {
78+
ctx, cancel := context.WithCancel(context.Background())
79+
defer cancel()
80+
81+
gCtx, gCancel := context.WithCancel(context.Background())
82+
gCancel()
83+
84+
g, _ := errgroup.WithContext(gCtx)
85+
86+
errs := make(chan error)
87+
go func() {
88+
defer close(errs)
89+
90+
for _, e := range st.input {
91+
if l.latency > 0 {
92+
select {
93+
case <-time.After(l.latency):
94+
case <-ctx.Done():
95+
return
96+
}
97+
}
98+
99+
select {
100+
case errs <- e:
101+
case <-ctx.Done():
102+
return
103+
}
104+
}
105+
}()
106+
107+
ErrgroupReceive(g, errs)
108+
if err := g.Wait(); st.error {
109+
require.Error(t, err)
110+
} else {
111+
require.NoError(t, err)
112+
}
113+
})
114+
}
115+
})
116+
}
117+
}
118+
119+
func TestCopyFirst(t *testing.T) {
120+
subtests := []struct {
121+
name string
122+
io []string
123+
error bool
124+
}{
125+
{"empty", nil, true},
126+
{"one", []string{"a"}, false},
127+
{"two", []string{"a", "b"}, false},
128+
{"three", []string{"a", "b", "c"}, false},
129+
}
130+
131+
latencies := []struct {
132+
name string
133+
latency time.Duration
134+
}{
135+
{"instantly", 0},
136+
{"1us", time.Microsecond},
137+
{"20ms", 20 * time.Millisecond},
138+
}
139+
140+
for _, st := range subtests {
141+
t.Run(st.name, func(t *testing.T) {
142+
for _, l := range latencies {
143+
t.Run(l.name, func(t *testing.T) {
144+
ctx, cancel := context.WithCancel(context.Background())
145+
defer cancel()
146+
147+
ch := make(chan string)
148+
go func() {
149+
defer close(ch)
150+
151+
for _, v := range st.io {
152+
if l.latency > 0 {
153+
select {
154+
case <-time.After(l.latency):
155+
case <-ctx.Done():
156+
return
157+
}
158+
}
159+
160+
select {
161+
case ch <- v:
162+
case <-ctx.Done():
163+
return
164+
}
165+
}
166+
}()
167+
168+
first, forward, err := CopyFirst(ctx, ch)
169+
if st.error {
170+
require.Error(t, err)
171+
require.Nil(t, forward, "forward should be nil")
172+
return
173+
}
174+
175+
require.NoError(t, err)
176+
require.NotNil(t, forward, "forward should not be nil")
177+
178+
expected := ""
179+
if len(st.io) > 0 {
180+
expected = st.io[0]
181+
}
182+
183+
require.Equal(t, expected, first, "first should be the first element")
184+
185+
for _, expected := range st.io {
186+
select {
187+
case actual, ok := <-forward:
188+
if !ok {
189+
require.Fail(t, "channel should not be closed")
190+
}
191+
192+
require.Equal(t, expected, actual, "forwarded element should match")
193+
case <-time.After(time.Second):
194+
require.Fail(t, "channel should not block")
195+
}
196+
}
197+
198+
select {
199+
case _, ok := <-forward:
200+
if ok {
201+
require.Fail(t, "channel should be closed")
202+
}
203+
case <-time.After(time.Second):
204+
require.Fail(t, "channel should not block")
205+
}
206+
})
207+
}
208+
})
209+
}
210+
211+
t.Run("cancel-ctx", func(t *testing.T) {
212+
ctx, cancel := context.WithCancel(context.Background())
213+
cancel()
214+
215+
first, forward, err := CopyFirst(ctx, make(chan int))
216+
217+
require.Error(t, err)
218+
require.Nil(t, forward)
219+
require.Empty(t, first)
220+
})
221+
}

0 commit comments

Comments
 (0)