Skip to content

Commit b968d21

Browse files
author
Dmitriy Matrenichev
committed
feat: add TryRecv and RecvWithContext functions
Both of this should simplify places where we are using `select` currently. Also fix flaky tests while we are at it. Signed-off-by: Dmitriy Matrenichev <[email protected]>
1 parent 476dfea commit b968d21

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

channel/recv.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
package channel
6+
7+
import "context"
8+
9+
// RecvWithContext tries to receive a value from a channel which is aborted if the context is canceled.
10+
//
11+
// Function returns true if the value was received, false if the context was canceled or the channel was closed.
12+
func RecvWithContext[T any](ctx context.Context, ch <-chan T) (T, bool) {
13+
select {
14+
case <-ctx.Done():
15+
var zero T
16+
17+
return zero, false
18+
case val, ok := <-ch:
19+
if !ok {
20+
return val, false
21+
}
22+
23+
return val, true
24+
}
25+
}
26+
27+
// RecvState is the state of a channel after receiving a value.
28+
type RecvState int
29+
30+
const (
31+
// StateRecv means that a value was received from the channel.
32+
StateRecv RecvState = iota
33+
// StateEmpty means that the channel was empty.
34+
StateEmpty
35+
// StateClosed means that the channel was closed.
36+
StateClosed
37+
)
38+
39+
// TryRecv tries to receive a value from a channel.
40+
//
41+
// Function returns the value and the state of the channel.
42+
func TryRecv[T any](ch <-chan T) (T, RecvState) {
43+
var zero T
44+
45+
select {
46+
case val, ok := <-ch:
47+
if !ok {
48+
return zero, StateClosed
49+
}
50+
51+
return val, StateRecv
52+
default:
53+
return zero, StateEmpty
54+
}
55+
}

channel/recv_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
package channel_test
6+
7+
import (
8+
"context"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
13+
"github.com/siderolabs/gen/channel"
14+
)
15+
16+
func TestRecvWithContext(t *testing.T) {
17+
t.Parallel()
18+
19+
ch := make(chan int, 1)
20+
ch <- 42
21+
22+
ctx, cancel := context.WithCancel(context.Background())
23+
t.Cleanup(cancel)
24+
25+
val, ok := channel.RecvWithContext(ctx, ch)
26+
assert.Equal(t, 42, val)
27+
assert.True(t, ok)
28+
29+
cancel()
30+
31+
val, ok = channel.RecvWithContext(ctx, ch)
32+
assert.Zero(t, val)
33+
assert.False(t, ok)
34+
}
35+
36+
func TestRecvWithContextCloseCh(t *testing.T) {
37+
t.Parallel()
38+
39+
ch := make(chan int, 1)
40+
ch <- 42
41+
42+
ctx := context.Background()
43+
44+
val, ok := channel.RecvWithContext(ctx, ch)
45+
assert.Equal(t, 42, val)
46+
assert.True(t, ok)
47+
48+
close(ch)
49+
val, ok = channel.RecvWithContext(ctx, ch)
50+
assert.Zero(t, val)
51+
assert.False(t, ok)
52+
}
53+
54+
func TestTryRecv(t *testing.T) {
55+
t.Parallel()
56+
57+
ch := make(chan int, 1)
58+
ch <- 42
59+
60+
val, state := channel.TryRecv(ch)
61+
assert.Equal(t, 42, val)
62+
assert.Equal(t, channel.StateRecv, state)
63+
64+
val, state = channel.TryRecv(ch)
65+
assert.Zero(t, val)
66+
assert.Equal(t, channel.StateEmpty, state)
67+
68+
close(ch)
69+
val, state = channel.TryRecv(ch)
70+
assert.Zero(t, val)
71+
assert.Equal(t, channel.StateClosed, state)
72+
}

channel/send_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
)
1515

1616
func TestSendWithContext(t *testing.T) {
17+
t.Parallel()
18+
1719
ch := make(chan int, 1)
1820
ctx, cancel := context.WithCancel(context.Background())
1921
t.Cleanup(cancel)

containers/lazymap_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package containers_test
77

88
import (
99
"fmt"
10+
"sort"
1011
"testing"
1112

1213
"github.com/stretchr/testify/assert"
@@ -132,6 +133,9 @@ func TestLazyBiMap(t *testing.T) {
132133
values = append(values, v)
133134
})
134135

136+
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
137+
sort.Slice(values, func(i, j int) bool { return values[i] < values[j] })
138+
135139
assert.Equal(t, []int{1, 2}, keys)
136140
assert.Equal(t, []int{100, 200}, values)
137141
})
@@ -227,6 +231,9 @@ func TestLazyMap(t *testing.T) {
227231
values = append(values, v)
228232
})
229233

234+
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
235+
sort.Slice(values, func(i, j int) bool { return values[i] < values[j] })
236+
230237
assert.Equal(t, []int{4, 5}, keys)
231238
assert.Equal(t, []int{400, 500}, values)
232239
})

0 commit comments

Comments
 (0)