PyHamcrest icon indicating copy to clipboard operation
PyHamcrest copied to clipboard

hamcrest.contains_inanyorder is order sensitive

Open robertwb opened this issue 5 years ago • 5 comments

E.g. assert_that([1, 2], contains_inanyorder(anything(), 1)) fails.

robertwb avatar Oct 17 '19 18:10 robertwb

Hmmmmm. I can see what's going on here. It's not that contains_inanyorder is order sensitive, it's that overgeneral matchers can consume items from the actuals, leaving more specific matchers to miss.

In your example, the anything() matches the 1 from the actual list. The contains_inanyorder matcher pairs them off, drops them, then moves on, leaving the 2 and the 1 from the matcher list, which of course don't match.

I'm not sure how I'd approach fixing this. Any ideas, @robertwb?

brunns avatar Oct 21 '19 13:10 brunns

In the meantime, the matcher is working as documented, and you can support your use case by specifying the more specific matchers first, then the less specific.

brunns avatar Oct 21 '19 13:10 brunns

Yes, in this toy example it's clear to see what the order could be, but that's not always the case.

This could be solved generally with https://en.wikipedia.org/wiki/Maximum_cardinality_matching

robertwb avatar Oct 22 '19 04:10 robertwb

Interesting, but I'm not sure I think the complexity is worthwhile. How often is this an issue? If we implemented a maximum cardinality matching algorithm, how would that work with consume-once actuals, such as generators?

In any case, I won't get to look at it until #117 is in.

brunns avatar Oct 22 '19 09:10 brunns

I've run into it a couple of times, the latest because the set of matchers is derived from a container with non-deterministic ordering. For consume-once actuals, one would either run it against all matchers while iterating (rather than just until one finds a match) or reify it into a list.

robertwb avatar Oct 23 '19 03:10 robertwb