Skip to content

Commit 49e46f7

Browse files
committed
feat(Flatten): allow func(T, X...) V|(V, bool) function signatures
Allowing to use td.Re as function for example: ok = td.Cmp(t, strings.Split(got, "\n"), td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re))) Signed-off-by: Maxime Soulé <[email protected]>
1 parent 9e24789 commit 49e46f7

File tree

6 files changed

+127
-3
lines changed

6 files changed

+127
-3
lines changed

td/example_cmp_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,6 +2442,39 @@ func ExampleCmpRe_capture() {
24422442
// false
24432443
}
24442444

2445+
func ExampleCmpRe_multilines() {
2446+
t := &testing.T{}
2447+
2448+
got := `multi
2449+
lines
2450+
probably
2451+
more
2452+
than 4
2453+
`
2454+
expectedRe := `^multi
2455+
lines?
2456+
(probably|possibly)
2457+
more
2458+
than \d+
2459+
\z`
2460+
2461+
ok := td.CmpRe(t, got, expectedRe, nil)
2462+
fmt.Println("Raw multi-lines string matches:", ok)
2463+
2464+
// But for strings with many, many, many lines, when the regexp
2465+
// doesn't match, it is sometimes difficult to see where the regexp
2466+
// failed in the string. Here td.List & td.Flatten can help to apply
2467+
// regexp line per line:
2468+
ok = td.Cmp(t,
2469+
strings.Split(got, "\n"),
2470+
td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
2471+
fmt.Println("All string lines match:", ok)
2472+
2473+
// Output:
2474+
// Raw multi-lines string matches: true
2475+
// All string lines match: true
2476+
}
2477+
24452478
func ExampleCmpRe_compiled() {
24462479
t := &testing.T{}
24472480

td/example_t_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,6 +2442,39 @@ func ExampleT_Re_capture() {
24422442
// false
24432443
}
24442444

2445+
func ExampleT_Re_multilines() {
2446+
t := td.NewT(&testing.T{})
2447+
2448+
got := `multi
2449+
lines
2450+
probably
2451+
more
2452+
than 4
2453+
`
2454+
expectedRe := `^multi
2455+
lines?
2456+
(probably|possibly)
2457+
more
2458+
than \d+
2459+
\z`
2460+
2461+
ok := t.Re(got, expectedRe, nil)
2462+
fmt.Println("Raw multi-lines string matches:", ok)
2463+
2464+
// But for strings with many, many, many lines, when the regexp
2465+
// doesn't match, it is sometimes difficult to see where the regexp
2466+
// failed in the string. Here td.List & td.Flatten can help to apply
2467+
// regexp line per line:
2468+
ok = t.Cmp(
2469+
strings.Split(got, "\n"),
2470+
td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
2471+
fmt.Println("All string lines match:", ok)
2472+
2473+
// Output:
2474+
// Raw multi-lines string matches: true
2475+
// All string lines match: true
2476+
}
2477+
24452478
func ExampleT_Re_compiled() {
24462479
t := td.NewT(&testing.T{})
24472480

td/example_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2709,6 +2709,39 @@ func ExampleRe_capture() {
27092709
// false
27102710
}
27112711

2712+
func ExampleRe_multilines() {
2713+
t := &testing.T{}
2714+
2715+
got := `multi
2716+
lines
2717+
probably
2718+
more
2719+
than 4
2720+
`
2721+
expectedRe := `^multi
2722+
lines?
2723+
(probably|possibly)
2724+
more
2725+
than \d+
2726+
\z`
2727+
2728+
ok := td.Cmp(t, got, td.Re(expectedRe))
2729+
fmt.Println("Raw multi-lines string matches:", ok)
2730+
2731+
// But for strings with many, many, many lines, when the regexp
2732+
// doesn't match, it is sometimes difficult to see where the regexp
2733+
// failed in the string. Here td.List & td.Flatten can help to apply
2734+
// regexp line per line:
2735+
ok = td.Cmp(t,
2736+
strings.Split(got, "\n"),
2737+
td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
2738+
fmt.Println("All string lines match:", ok)
2739+
2740+
// Output:
2741+
// Raw multi-lines string matches: true
2742+
// All string lines match: true
2743+
}
2744+
27122745
func ExampleReAll_capture() {
27132746
t := &testing.T{}
27142747

td/flatten.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,14 @@ import (
100100
//
101101
// func(T) V
102102
// func(T) (V, bool)
103+
// func(T, X...) V
104+
// func(T, X...) (V, bool)
103105
//
104106
// T can be the same as V, but it is not mandatory. The (V, bool)
105-
// returned case allows to exclude some items when returning false.
107+
// returned cases allow to exclude some items when returning
108+
// false. For the variadic cases, X does not matter as the function is
109+
// always called without any variadic argument. See [Re] for an
110+
// example of use.
106111
//
107112
// If the function signature does not match these cases, Flatten panics.
108113
//
@@ -222,7 +227,8 @@ func Flatten(sliceOrMap any, fn ...any) flat.Slice {
222227
vfn := reflect.ValueOf(f)
223228

224229
if fnType.Kind() != reflect.Func ||
225-
fnType.NumIn() != 1 || fnType.IsVariadic() ||
230+
!(fnType.NumIn() == 1 && !fnType.IsVariadic() ||
231+
fnType.NumIn() == 2 && fnType.IsVariadic()) ||
226232
(fnType.NumOut() != 1 && (fnType.NumOut() != 2 || fnType.Out(1) != types.Bool)) {
227233
panic(color.BadUsage(usageFunc, f, 2, false))
228234
}

td/flatten_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ func TestFlatten(t *testing.T) {
129129
int8(15), int8(18), int8(21), int8(24), int8(27),
130130
},
131131
},
132+
{
133+
name: "any+variadic",
134+
fn: func(a any, opts ...bool) int {
135+
x, _ := a.(int)
136+
return x
137+
},
138+
expected: []any{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
139+
},
132140
}
133141
for _, tc := range testCases {
134142
t.Run(tc.name, func(t *testing.T) {

td/td_re.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018, Maxime Soulé
1+
// Copyright (c) 2018-2025, Maxime Soulé
22
// All rights reserved.
33
//
44
// This source code is licensed under the BSD-style license found in the
@@ -93,6 +93,17 @@ func newRe(regIf any, capture ...any) *tdRe {
9393
// td.Cmp(t, "John Doe",
9494
// td.Re(`^(\w+) (\w+)`, td.Bag("Doe", "John"))) // succeeds
9595
//
96+
// When matching many, many lines with a single regexp, it is
97+
// sometimes difficult to see where the regexp failed in the input
98+
// string. To avoid that, the regexp can be split by lines and so the
99+
// failure is easier to locate, thanks to [List] operator and [Flatten]:
100+
//
101+
// td.Cmp(t,
102+
// strings.Split(got, "\n"),
103+
// td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
104+
//
105+
// See also the multilines example below.
106+
//
96107
// See also [ReAll].
97108
func Re(reg any, capture ...any) TestDeep {
98109
r := newRe(reg, capture...)

0 commit comments

Comments
 (0)