Skip to content

Commit f64538b

Browse files
committed
Add EqualLength, AtLeastLength
1 parent e2a0992 commit f64538b

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ be.NotIn(t, "\x00", []byte("\a\b\x00\r\t")) // bad
6464
// t.Fatal("\x00" in "\a\b\x00\r\t")
6565
```
6666

67+
Check how long something rangeable is:
68+
69+
```
70+
seq := strings.FieldsSeq("1 2 3 4")
71+
be.EqualLength(t, seq, 4) // good
72+
be.EqualLength(t, seq, 1) // bad
73+
be.AtLeastLength(t, seq, 1) // good
74+
be.AtLeastLength(t, seq, 5) // bad
75+
be.AtLeastLength(t, "123", 3) // good
76+
be.AtLeastLength(t, "123", 4) // bad
77+
```
78+
6779
Test anything else:
6880

6981
```go

be_example_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package be_test
22

33
import (
44
"errors"
5+
"strings"
56

67
"github.com/carlmjohnson/be"
78
)
@@ -34,6 +35,14 @@ func Example() {
3435
be.NotIn(t, "\x01", []byte("\a\b\x00\r\t")) // good
3536
be.NotIn(t, "\x00", []byte("\a\b\x00\r\t")) // bad
3637

38+
seq := strings.FieldsSeq("1 2 3 4")
39+
be.EqualLength(t, 4, seq) // good
40+
be.EqualLength(t, 1, seq) // bad
41+
be.AtLeastLength(t, 1, seq) // good
42+
be.AtLeastLength(t, 5, seq) // bad
43+
be.AtLeastLength(t, 3, "123") // good
44+
be.AtLeastLength(t, 4, "123") // bad
45+
3746
// Output:
3847
// want: hello; got: world
3948
// got: goodbye
@@ -42,4 +51,7 @@ func Example() {
4251
// got: (O_o)
4352
// "World" not in "hello, world"
4453
// "\x00" in "\a\b\x00\r\t"
54+
// want len(seq) == 1; got at least 2
55+
// want len(seq) >= 5; got 4
56+
// want len(seq) >= 4; got 3
4557
}

be_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"io"
7+
"maps"
78
"strings"
89
"testing"
910
"time"
@@ -43,6 +44,14 @@ func Test(t *testing.T) {
4344
beOkay(func(tb testing.TB) { be.NilErr(tb, nil) })
4445
beOkay(func(tb testing.TB) { be.True(tb, true) })
4546
beOkay(func(tb testing.TB) { be.False(tb, false) })
47+
beOkay(func(tb testing.TB) { be.EqualLength(tb, 0, map[int]int{}) })
48+
beOkay(func(tb testing.TB) { be.EqualLength(tb, 1, map[int]int{1: 1}) })
49+
ch := make(chan int, 1)
50+
beOkay(func(tb testing.TB) { be.EqualLength(tb, 0, ch) })
51+
ch <- 1
52+
beOkay(func(tb testing.TB) { be.EqualLength(tb, 1, ch) })
53+
seq2 := maps.All(map[int]int{1: 1})
54+
beOkay(func(tb testing.TB) { be.EqualLength(tb, 1, seq2) })
4655
beBad := func(callback func(tb testing.TB)) {
4756
t.Helper()
4857
var buf strings.Builder
@@ -62,4 +71,9 @@ func Test(t *testing.T) {
6271
beBad(func(tb testing.TB) { be.NilErr(tb, errors.New("")) })
6372
beBad(func(tb testing.TB) { be.True(tb, false) })
6473
beBad(func(tb testing.TB) { be.False(tb, true) })
74+
beBad(func(tb testing.TB) { be.EqualLength(tb, 1, ch) })
75+
ch <- 1
76+
beBad(func(tb testing.TB) { be.EqualLength(tb, 0, ch) })
77+
close(ch)
78+
beBad(func(tb testing.TB) { be.EqualLength(tb, 1, ch) })
6579
}

len.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package be
2+
3+
import (
4+
"iter"
5+
"reflect"
6+
"slices"
7+
"testing"
8+
)
9+
10+
// EqualLength calls t.Fatalf if seq has a length that is not exactly want.
11+
// The type of seq must be array, array pointer, slice, map, string, channel, [iter.Seq], or [iter.Seq2].
12+
// For channels and iterators, the values are consumed to get the sequence length.
13+
func EqualLength(t testing.TB, want int, seq any) {
14+
t.Helper()
15+
getLen(t, seq, want, false)
16+
}
17+
18+
func getLen(t testing.TB, seq any, n int, atLeast bool) {
19+
rv := reflect.ValueOf(seq)
20+
rt := rv.Type()
21+
kind := rt.Kind()
22+
switch {
23+
case slices.Contains([]reflect.Kind{
24+
reflect.Array, reflect.Slice, reflect.Map, reflect.String,
25+
}, kind) ||
26+
(kind == reflect.Pointer && rt.Elem().Kind() == reflect.Array):
27+
compareN(t, n, rv.Len(), atLeast)
28+
case kind == reflect.Chan:
29+
eqchan(t, rv, n, atLeast)
30+
case kind == reflect.Func && rt.CanSeq():
31+
eqseq(t, rv, n, atLeast)
32+
case kind == reflect.Func && rt.CanSeq2():
33+
eqseq2(t, rv, n, atLeast)
34+
default:
35+
panic("seq must be a non-integer rangeable type")
36+
}
37+
}
38+
39+
func eqchan(t testing.TB, rv reflect.Value, n int, atLeast bool) {
40+
i := 0
41+
for ; i <= n; i++ {
42+
chosen, _, recvOK := reflect.Select([]reflect.SelectCase{
43+
{Dir: reflect.SelectRecv, Chan: rv},
44+
{Dir: reflect.SelectDefault},
45+
})
46+
47+
if chosen == 1 || !recvOK { // default case or channel closed
48+
break
49+
}
50+
}
51+
compareN(t, n, i, atLeast)
52+
}
53+
54+
func eqseq(t testing.TB, rv reflect.Value, n int, atLeast bool) {
55+
next, stop := iter.Pull(rv.Seq())
56+
defer stop()
57+
i := 0
58+
for ; i <= n; i++ {
59+
if _, ok := next(); !ok {
60+
break
61+
}
62+
}
63+
compareN(t, n, i, atLeast)
64+
}
65+
66+
func eqseq2(t testing.TB, rv reflect.Value, n int, atLeast bool) {
67+
next, stop := iter.Pull2(rv.Seq2())
68+
defer stop()
69+
i := 0
70+
for ; i <= n; i++ {
71+
if _, _, ok := next(); !ok {
72+
break
73+
}
74+
}
75+
compareN(t, n, i, atLeast)
76+
}
77+
78+
func compareN(t testing.TB, want, got int, atLeast bool) {
79+
switch {
80+
case atLeast && want > got:
81+
t.Fatalf("want len(seq) >= %d; got %d", want, got)
82+
case !atLeast && want > got:
83+
t.Fatalf("want len(seq) == %d; got %d", want, got)
84+
case !atLeast && want < got:
85+
t.Fatalf("want len(seq) == %d; got at least %d", want, got)
86+
}
87+
}
88+
89+
// AtLeastLength calls t.Fatalf if seq has a length that is not at least want.
90+
// The type of seq must be array, array pointer, slice, map, string, channel, [iter.Seq], or [iter.Seq2].
91+
// For channels and iterators, the values are consumed to get the sequence length.
92+
func AtLeastLength(t testing.TB, want int, seq any) {
93+
t.Helper()
94+
getLen(t, seq, want, true)
95+
}

0 commit comments

Comments
 (0)