onsi / gomega

Ginkgo's Preferred Matcher Library
http://onsi.github.io/gomega/
MIT License
2.15k stars 282 forks source link

RFC: Matching a sequence of actuals, in no particular order #683

Open thediveo opened 1 year ago

thediveo commented 1 year ago

Another one from the "odd stuff" department: from time to time I need to receive several things from a channel, but the order of them might be undefined. Such as events overtaking each other.

And to be honest, I ever wanted to give MakeMatcher a try! 👍🏼

My first attempt is an All(...) matcher to be used with Receive():

// All succeeds only after it has been shown a sequence of actual values that
// “ticked off” the specified matchers in no particular order. All will return
// an error in case it encounters an actual value not matching any of the
// specified matchers. If the same match should occur multiple times it needs to
// be specified as many times as required.
func All(matchers ...types.GomegaMatcher) types.GomegaMatcher {
    // Now that's fun to keep state not in a struct "object" but instead capture
    // state in a closure.
    remaining := slices.Clone(matchers)
    return MakeMatcher(func(actual any) (bool, error) {
        for idx, matcher := range remaining {
            succeeded, err := matcher.Match(actual)
            if err != nil {
                return false, err
            }
            if !succeeded {
                continue // ...maybe another one will match...?
            }
            remaining = slices.Delete(remaining, idx, idx+1)
            if len(remaining) > 0 {
                return false, nil
            }
            return true, nil
        }
        return false, errors.New("not in expected set")
    }).WithTemplate("Expected:\n{{.FormattedActual}}\n{{.To}} complete set")
}

However, the All matcher is a little bit off the beaten matcher path, as it does not keep silent when it doesn't match, but instead throws an error. It's like the temporal version of Contains(...) (on arrays, slices and maps), but this time on channel receives.

Any suggestion as to how to improve the error reporting in this case?