Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions td/example_cmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2442,6 +2442,39 @@ func ExampleCmpRe_capture() {
// false
}

func ExampleCmpRe_multilines() {
t := &testing.T{}

got := `multi
lines
probably
more
than 4
`
expectedRe := `^multi
lines?
(probably|possibly)
more
than \d+
\z`

ok := td.CmpRe(t, got, expectedRe, nil)
fmt.Println("Raw multi-lines string matches:", ok)

// But for strings with many, many, many lines, when the regexp
// doesn't match, it is sometimes difficult to see where the regexp
// failed in the string. Here td.List & td.Flatten can help to apply
// regexp line per line:
ok = td.Cmp(t,
strings.Split(got, "\n"),
td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
fmt.Println("All string lines match:", ok)

// Output:
// Raw multi-lines string matches: true
// All string lines match: true
}

func ExampleCmpRe_compiled() {
t := &testing.T{}

Expand Down
33 changes: 33 additions & 0 deletions td/example_t_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2442,6 +2442,39 @@ func ExampleT_Re_capture() {
// false
}

func ExampleT_Re_multilines() {
t := td.NewT(&testing.T{})

got := `multi
lines
probably
more
than 4
`
expectedRe := `^multi
lines?
(probably|possibly)
more
than \d+
\z`

ok := t.Re(got, expectedRe, nil)
fmt.Println("Raw multi-lines string matches:", ok)

// But for strings with many, many, many lines, when the regexp
// doesn't match, it is sometimes difficult to see where the regexp
// failed in the string. Here td.List & td.Flatten can help to apply
// regexp line per line:
ok = t.Cmp(
strings.Split(got, "\n"),
td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
fmt.Println("All string lines match:", ok)

// Output:
// Raw multi-lines string matches: true
// All string lines match: true
}

func ExampleT_Re_compiled() {
t := td.NewT(&testing.T{})

Expand Down
33 changes: 33 additions & 0 deletions td/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2709,6 +2709,39 @@ func ExampleRe_capture() {
// false
}

func ExampleRe_multilines() {
t := &testing.T{}

got := `multi
lines
probably
more
than 4
`
expectedRe := `^multi
lines?
(probably|possibly)
more
than \d+
\z`

ok := td.Cmp(t, got, td.Re(expectedRe))
fmt.Println("Raw multi-lines string matches:", ok)

// But for strings with many, many, many lines, when the regexp
// doesn't match, it is sometimes difficult to see where the regexp
// failed in the string. Here td.List & td.Flatten can help to apply
// regexp line per line:
ok = td.Cmp(t,
strings.Split(got, "\n"),
td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
fmt.Println("All string lines match:", ok)

// Output:
// Raw multi-lines string matches: true
// All string lines match: true
}

func ExampleReAll_capture() {
t := &testing.T{}

Expand Down
18 changes: 14 additions & 4 deletions td/flatten.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import (
"github.com/maxatome/go-testdeep/internal/types"
)

func flattenFuncIsValid(typ reflect.Type) bool {
return typ.Kind() == reflect.Func &&
(typ.NumIn() == 1 && !typ.IsVariadic() ||
typ.NumIn() == 2 && typ.IsVariadic()) &&
(typ.NumOut() == 1 || typ.NumOut() == 2 && typ.Out(1) == types.Bool)
}

// Flatten allows to flatten any slice, array or map in parameters of
// operators expecting ...any. fn parameter allows to filter and/or
// transform items before flattening and is described below.
Expand Down Expand Up @@ -100,9 +107,14 @@ import (
//
// func(T) V
// func(T) (V, bool)
// func(T, X...) V
// func(T, X...) (V, bool)
//
// T can be the same as V, but it is not mandatory. The (V, bool)
// returned case allows to exclude some items when returning false.
// returned cases allow to exclude some items when returning
// false. For the variadic cases, X does not matter as the function is
// always called without any variadic argument. See [Re] for an
// example of use.
//
// If the function signature does not match these cases, Flatten panics.
//
Expand Down Expand Up @@ -221,9 +233,7 @@ func Flatten(sliceOrMap any, fn ...any) flat.Slice {
fnType := reflect.TypeOf(f)
vfn := reflect.ValueOf(f)

if fnType.Kind() != reflect.Func ||
fnType.NumIn() != 1 || fnType.IsVariadic() ||
(fnType.NumOut() != 1 && (fnType.NumOut() != 2 || fnType.Out(1) != types.Bool)) {
if !flattenFuncIsValid(fnType) {
panic(color.BadUsage(usageFunc, f, 2, false))
}
if vfn.IsNil() {
Expand Down
8 changes: 8 additions & 0 deletions td/flatten_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ func TestFlatten(t *testing.T) {
int8(15), int8(18), int8(21), int8(24), int8(27),
},
},
{
name: "any+variadic",
fn: func(a any, opts ...bool) int {
x, _ := a.(int)
return x
},
expected: []any{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
13 changes: 12 additions & 1 deletion td/td_re.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018, Maxime Soulé
// Copyright (c) 2018-2025, Maxime Soulé
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
Expand Down Expand Up @@ -93,6 +93,17 @@ func newRe(regIf any, capture ...any) *tdRe {
// td.Cmp(t, "John Doe",
// td.Re(`^(\w+) (\w+)`, td.Bag("Doe", "John"))) // succeeds
//
// When matching many, many lines with a single regexp, it is
// sometimes difficult to see where the regexp failed in the input
// string. To avoid that, the regexp can be split by lines and so the
// failure is easier to locate, thanks to [List] operator and [Flatten]:
//
// td.Cmp(t,
// strings.Split(got, "\n"),
// td.List(td.Flatten(strings.Split(expectedRe, "\n"), td.Re)))
//
// See also the multilines example below.
//
// See also [ReAll].
func Re(reg any, capture ...any) TestDeep {
r := newRe(reg, capture...)
Expand Down